From df3107344d730ae92167bbd310ae5e2d8a4eab55 Mon Sep 17 00:00:00 2001 From: Space One Date: Sat, 11 Jul 2026 02:44:18 +0200 Subject: [PATCH 1/5] feat: add support for new QUERY method (RFC 10008) [RFC 10008](https://datatracker.ietf.org/doc/html/rfc10008) --- README.md | 2 + httoop/messages/method.py | 4 +- rfc/http/rfc10008.txt | 1169 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1173 insertions(+), 2 deletions(-) create mode 100644 rfc/http/rfc10008.txt diff --git a/README.md b/README.md index c86b27d4..3407e121 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ HTTP and extensions are defined in the following RFC's: * JavaScript Object Notation (JSON) Patch ([RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)) +* The HTTP QUERY Method ([RFC 10008](https://datatracker.ietf.org/doc/html/rfc10008)) + * Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP) ([RFC 6266](https://datatracker.ietf.org/doc/html/rfc6266)) * Upgrading to TLS Within HTTP/1.1 ([RFC 2817](https://datatracker.ietf.org/doc/html/rfc2817)) diff --git a/httoop/messages/method.py b/httoop/messages/method.py index c9bda1b2..a1f41a3c 100644 --- a/httoop/messages/method.py +++ b/httoop/messages/method.py @@ -29,8 +29,8 @@ def safe(self) -> bool: def idempotent(self) -> bool: return self in self.idempotent_methods - safe_methods = ('GET', 'HEAD', 'SEARCH') - idempotent_methods = ('GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE', 'SEARCH') + safe_methods = ('GET', 'HEAD', 'SEARCH', 'QUERY') + idempotent_methods = ('GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE', 'SEARCH', 'QUERY') METHOD_RE = re.compile(rb'^[A-Z0-9$_.-]{1,20}\Z', re.IGNORECASE) def __init__(self, method: str | None = None) -> None: diff --git a/rfc/http/rfc10008.txt b/rfc/http/rfc10008.txt new file mode 100644 index 00000000..45739226 --- /dev/null +++ b/rfc/http/rfc10008.txt @@ -0,0 +1,1169 @@ + + + + +Internet Engineering Task Force (IETF) J. Reschke +Request for Comments: 10008 greenbytes +Category: Standards Track J.M. Snell +ISSN: 2070-1721 Cloudflare + M. Bishop + Akamai + June 2026 + + + The HTTP QUERY Method + +Abstract + + This specification defines the QUERY method for HTTP. A QUERY + requests that the request target process the enclosed content in a + safe and idempotent manner and then respond with the result of that + processing. This is similar to POST requests, but QUERY requests can + be automatically repeated or restarted without concern for partial + state changes. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 7841. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + https://www.rfc-editor.org/info/rfc10008. + +Copyright Notice + + Copyright (c) 2026 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Revised BSD License text as described in Section 4.e of the + Trust Legal Provisions and are provided without warranty as described + in the Revised BSD License. + +Table of Contents + + 1. Introduction + 1.1. Terminology + 1.2. Notational Conventions + 2. QUERY Method + 2.1. Media Types and Content Negotiation + 2.2. Equivalent Resource + 2.3. Content-Location Response Field + 2.4. Location Response Field + 2.5. Redirection + 2.6. Conditional Requests + 2.7. Caching + 2.8. Range Requests + 3. The Accept-Query Header Field + 4. Security Considerations + 5. IANA Considerations + 5.1. Registration of the QUERY Method + 5.2. Registration of the Accept-Query Field + 6. References + 6.1. Normative References + 6.2. Informative References + Appendix A. Examples + A.1. Simple Query + A.2. Discovery of QUERY Support + A.3. Discovery of QUERY Formats + A.4. Content-Location, Location, and Indirect Responses + A.4.1. Using Content-Location + A.4.2. Using Location + A.4.3. Indirect Responses + A.5. Conditional Requests + A.6. More Query Formats + Appendix B. Selection of the Method Name 'QUERY' + Acknowledgements + Contributors + Authors' Addresses + +1. Introduction + + This specification defines the HTTP QUERY request method as a means + of making a safe, idempotent request (Section 9.2 of [HTTP]) that + encloses a representation describing how the request is to be + processed by the target resource. + + A common query pattern is: + + GET /feed?q=foo&limit=10&sort=-published HTTP/1.1 + Host: example.org + + However, when the data conveyed is too voluminous to be encoded in + the request's URI, this pattern becomes problematic: + + * size limits often are not known ahead of time because a request + can pass through many uncoordinated systems (but note that + Section 4.1 of [HTTP] recommends senders and recipients to support + at least 8000 octets), + + * expressing certain kinds of data in the target URI is inefficient + because of the overhead of encoding that data into a valid URI, + + * request URIs are more likely to be logged than request content and + may also turn up in bookmarks, + + * encoding queries directly into the request URI effectively casts + every possible combination of query inputs as distinct resources. + + As an alternative to using GET, many implementations make use of the + HTTP POST method to perform queries, as illustrated in the example + below. In this case, the input to the query operation is passed as + the request content as opposed to using the request URI's query + component. + + A typical use of HTTP POST for requesting a query is: + + POST /feed HTTP/1.1 + Host: example.org + Content-Type: application/x-www-form-urlencoded + + q=foo&limit=10&sort=-published + + In this variation, however, it is not readily apparent -- without + specific knowledge of the resource and server to which the request is + being sent -- that a safe, idempotent query is being performed. + + The QUERY method provides a solution that spans the gap between the + use of GET and POST, with the example above being expressed as: + + QUERY /feed HTTP/1.1 + Host: example.org + Content-Type: application/x-www-form-urlencoded + + q=foo&limit=10&sort=-published + + As with POST, the input to the query operation is passed as the + content of the request rather than as part of the request URI. + Unlike POST, however, the method is explicitly safe and idempotent, + allowing functions like caching and automatic retries to operate. + + Recognizing the design principle that any important resource ought to + be identified by a URI, this specification describes how a server can + assign URIs to both the query itself or to a specific query result, + for later use in a GET request. + + Summarizing: + + +==========+=================+=================+===================+ + | |GET |QUERY | POST | + +==========+=================+=================+===================+ + |Safe |yes |yes | potentially no | + +----------+-----------------+-----------------+-------------------+ + |Idempotent|yes |yes | potentially no | + +----------+-----------------+-----------------+-------------------+ + |URI for |yes (by |optional | no | + |query |definition) |(Location | | + |itself | |response field) | | + +----------+-----------------+-----------------+-------------------+ + |URI for |optional |optional | optional | + |query |(Content-Location|(Content-Location| (Content-Location | + |result |response field) |response field) | response field) | + +----------+-----------------+-----------------+-------------------+ + |Cacheable |yes |yes | yes, but only for | + | | | | future GET or | + | | | | HEAD requests | + +----------+-----------------+-----------------+-------------------+ + |Content |"no defined |expected | expected | + |(body) |semantics" |(semantics per | (semantics per | + | | |target resource) | target resource) | + +----------+-----------------+-----------------+-------------------+ + + Table 1: Summary of Relevant Method Properties + +1.1. Terminology + + This document uses terminology defined in Section 3 of [HTTP]. + + Furthermore, it uses the terms _URI query parameter_ for parameters + in the query component of a URI (Section 4.2.2 of [HTTP]) and _query + content_ for the request content (Section 6.4 of [HTTP]) of a QUERY + request. + +1.2. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and + "OPTIONAL" in this document are to be interpreted as described in + BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all + capitals, as shown here. + +2. QUERY Method + + The QUERY method is used to initiate a server-side query. Unlike the + GET method, which requests a representation of the resource + identified by the target URI (as defined by Section 7.1 of [HTTP]), + the QUERY method is used to ask the target resource to perform a + query operation within the scope of that target resource. + + The content of the request and its media type define the query. The + origin server determines the scope of the operation based on the + target resource. + + Servers MUST fail the request if the Content-Type request field + ([HTTP], Section 8.3) is missing or is inconsistent with the request + content. + + As for all HTTP methods, the target URI's query part takes part in + identifying the resource being queried. Whether and how it directly + affects the result of the query is specific to the resource and is + out of scope for this specification. + + QUERY requests are safe with regard to the target resource ([HTTP], + Section 9.2.1); that is, the client does not request or expect any + change to the state of the target resource. This does not prevent + the server from creating additional HTTP resources through which + additional information can be retrieved (see Sections 2.3 and 2.4). + + Furthermore, QUERY requests are idempotent ([HTTP], Section 9.2.2); + they can be retried or repeated when needed, for instance, after a + connection failure. + + As per Section 15.3 of [HTTP], a 2xx (Successful) response code + signals that the request was successfully received, understood, and + accepted. + + In particular, a 200 (OK) response indicates that the query was + successfully processed and the results of that processing are + enclosed as the response content. + +2.1. Media Types and Content Negotiation + + The semantics of a QUERY request depend both on the request content + and the associated metadata, such as the media type ([HTTP], + Section 8.3.1). In general, any problem with requests where content + and metadata are inconsistent MUST be rejected with a 4xx (Client + Error) response ([HTTP], Section 15.5). + + The list below describes various cases of failures and recommends + specific status codes: + + * If a request lacks media type information, it is incorrect by + definition and needs to fail with a 4xx status code such as 400 + (Client Error). + + * If a media type is specified but is not supported by the resource, + a 415 (Unsupported Media Type) is appropriate. This specifically + includes the case where the media type is known in principle, but + it lacks semantics specific to a QUERY to the target resource. In + both cases, the Accept-Query response field (Section 3) can be + used to inform the client of the media types that are supported. + + * If a media type is specified but is inconsistent with the actual + request content, a 400 (Bad Request) can be returned. That is, a + server is not allowed to infer a media type from the request + content and then override a missing or "erroneous" value (i.e., + "content sniffing"). + + * If the media type is specified and understood, and the content is + indeed consistent with the type, but the query cannot be processed + due to the actual contents of the query, the status 422 + (Unprocessable Content) can be used. An example would be a + syntactically correct SQL query that identifies a non-existent + table. + + * If the client requests a specific response media type using the + Accept field ([HTTP], Section 12.5.1) that is not supported by the + resource, a status code of 406 (Not Acceptable) is appropriate. + +2.2. Equivalent Resource + + The _equivalent resource_ for any given QUERY request is a resource + that responds to GET requests, represents that QUERY request and its + target, and takes both message content and metadata into account + (Section 6 of [HTTP]). In particular, this includes representation + metadata (Section 8 of [HTTP]) such as the content's media type. + + In other words, the equivalent resource is derived from the resource + implementing QUERY by incorporating the request content. + + The term _equivalent resource_ is used as a means to define behavior + for other HTTP aspects, such as selected representations. Servers + can but do not have to assign URIs to these resources (see + Section 1.1 of [URI]). If they do so, these resources will become + accessible for GET requests. + +2.3. Content-Location Response Field + + A successful response (2xx, Section 15.3 of [HTTP]) can include a + Content-Location header field containing an identifier for a resource + corresponding to the results of the operation; see Section 8.7 of + [HTTP] for details. This represents a claim from the server that a + client can send a GET request for the indicated URI to retrieve the + results of the query operation just performed. The indicated + resource might be temporary. + + See Appendix A.4.1 for an example. + +2.4. Location Response Field + + A server can assign a URI to the equivalent resource (Section 2.2) of + a QUERY request. If the server does so, the URI of that resource can + be included in the Location header field of the 2xx response (see + Section 10.2.2 of [HTTP]). This represents a claim that a client can + send a GET request to the indicated URI to repeat the query operation + just performed without resending the query content. This resource's + URI might be temporary; if a future request fails, the client can + retry using the original QUERY request target and the previously + submitted content. + + See Appendix A.4.2 for an example. + +2.5. Redirection + + In some cases, the server may choose to respond indirectly to the + QUERY request by redirecting the user agent to a different URI (see + Section 15.4 of [HTTP]). + + A response with either status codes 301 (Moved Permanently, [HTTP], + Section 15.4.2) or 308 (Permanent Redirect, [HTTP], Section 15.4.9) + indicates that the target resource has permanently moved to a + different URI referenced by the Location response field ([HTTP], + Section 10.2.2). Likewise, a response with either status codes 302 + (Found, [HTTP], Section 15.4.3) or 307 (Temporary Redirect, [HTTP], + Section 15.4.8) indicates that the target resource has temporarily + moved. In all four cases, the server is suggesting that the user + agent can accomplish its original QUERY request by sending a similar + QUERY request to the new target URI referenced by the Location. + + Note that the exceptions for redirecting a POST as a GET request + after a 301 or 302 response do not apply to QUERY requests. + + A response to QUERY with the status code 303 (See Other, + Section 15.4.4 of [HTTP]) indicates that the original query can be + accomplished via a normal retrieval request on the URI referenced by + the Location response field ([HTTP], Section 10.2.2). For HTTP, this + means sending a GET request to the new target URI, as illustrated by + the example in Appendix A.4.3. + +2.6. Conditional Requests + + The selected representation (Section 3.2 of [HTTP]) of a QUERY + request is the same as for a GET request to the equivalent resource + (Section 2.2) of that QUERY request. + + A conditional QUERY requests that the selected representation (i.e., + the query results after any content negotiation) be returned in the + response only under the circumstances described by the conditional + header field(s), as defined in Section 13 of [HTTP]. + + See Appendix A.5 for examples. + +2.7. Caching + + The response to a QUERY method is cacheable; a cache MAY use it to + satisfy subsequent QUERY requests as per Section 4 of [HTTP-CACHING]. + + The cache key for a QUERY request (Section 2 of [HTTP-CACHING]) MUST + incorporate the request content (Section 6 of [HTTP-CACHING]) and + related metadata (Section 8 of [HTTP]). + + To improve cache efficiency, caches MAY remove semantically + insignificant differences from request content and related metadata + first. For instance, by: + + * removing content encoding(s) (Section 8.4 of [HTTP]). + + * normalizing based upon knowledge of format conventions, as + indicated by any media subtype suffix in the request's Content- + Type field (e.g., "+json"; see Section 4.2.8 of [RFC6838]). + + * normalizing based upon knowledge of the semantics of the content + itself, as indicated by the request's Content-Type field. + + Note that any such transformation is performed solely for the purpose + of generating a cache key; it does not change the request itself. + + Clients can indicate, using the "no-transform" cache directive + (Section 5.2.1.6 of [HTTP-CACHING]) that they wish that no such + transformation happens (but note that this directive is just + advisory). + + Note that caching QUERY method responses is inherently more complex + than caching responses to GET, as complete reading of the request's + content is needed in order to determine the cache key. If a QUERY + response supplies a Location response field (Section 2.4) to indicate + a URI for an equivalent resource (Section 2.2), clients can switch to + GET for subsequent requests, thereby simplifying processing. + +2.8. Range Requests + + The semantics of Range Requests for QUERY are identical to those for + GET, as defined in Section 14 of [HTTP]. Byte Range Requests (the + only range unit defined at the time of writing), however, offer + little value for the results of a QUERY request. + + Query formats often define their own way of limiting or paging + through result sets, such as with "FETCH FIRST ... ROWS ONLY" in SQL. + It is expected that these built-in features will be used instead of + HTTP Range Requests. + +3. The Accept-Query Header Field + + The "Accept-Query" response header field can be used by a resource to + directly signal support for the QUERY method while identifying the + specific query format media type(s) that may be used. + + Accept-Query contains a list of media ranges (Section 12.5.1 of + [HTTP]) using "Structured Fields" syntax [STRUCTURED-FIELDS]. Media + ranges are represented by a List Structured Header Field of either + Tokens or Strings, containing the media range value without + parameters. + + Media type parameters, if any, are mapped to Structured Field + Parameters with the String or Token type. The choice of Token versus + String is semantically insignificant. That is, recipients MAY + convert Tokens to Strings, but MUST NOT process them differently + based on the received type. + + Media types do not exactly map to Tokens; for instance, they allow a + leading digit. In cases like these, the String format needs to be + used. + + The only supported uses of wildcards are "*/*", which matches any + type, or "xxxx/*", which matches any subtype of the indicated type. + + The order of types listed in the field value is not significant. + + The value of the Accept-Query field applies to every URI on the + server that shares the same path; in other words, the query component + is ignored. If requests to the same resource return different + Accept-Query values, the most recently received fresh value (per + Section 4.2 of [HTTP-CACHING]) is used. + + For example: + + Accept-Query: "application/jsonpath", application/sql;charset="UTF-8" + + Although the syntax for this field appears to be similar to other + fields, such as "Accept" (Section 12.5.1 of [HTTP]), it is a + Structured Field and thus MUST be processed as specified in Section 4 + of [STRUCTURED-FIELDS]. + +4. Security Considerations + + The QUERY method is subject to the same general security + considerations as all HTTP methods as described in [HTTP]. + + It can be used as an alternative to passing request information in + the URI (e.g., in the query component). This is preferred in some + cases, as the URI is more likely to be logged or otherwise processed + by intermediaries than the request content. In other cases, where + the query contains sensitive information, the potential for logging + of the URI might motivate the use of QUERY over GET. + + If a server creates a temporary resource to represent the results of + a QUERY request (e.g., for use in the Location or Content-Location + field), assigns a URI to that resource, and the request contains + sensitive information that cannot be logged, then that URI SHOULD be + chosen such that it does not include any sensitive portions of the + original request content. + + Caches that normalize QUERY content incorrectly or in ways that are + significantly different from how the resource processes the content + can return an incorrect response if normalization results in a false + positive. + + A QUERY request from user agents implementing Cross-Origin Resource + Sharing (CORS) will require a "preflight" request, as QUERY does not + belong to the set of CORS-safelisted methods (see [FETCH]). + +5. IANA Considerations + +5.1. Registration of the QUERY Method + + IANA has added the QUERY method to the "Hypertext Transfer Protocol + (HTTP) Method Registry" at (see Section 16.3.1 of [HTTP]). + + +=============+======+============+========================+ + | Method Name | Safe | Idempotent | Specification | + +=============+======+============+========================+ + | QUERY | yes | yes | Section 2 of RFC 10008 | + +-------------+------+------------+------------------------+ + + Table 2: QUERY Method Definition + +5.2. Registration of the Accept-Query Field + + IANA has added the Accept-Query field to the "Hypertext Transfer + Protocol (HTTP) Field Name Registry" at + (see Section 16.1.1 of + [HTTP]). + + +==============+===========+============+==============+==========+ + | Field Name | Status | Structured | Reference | Comments | + | | | Type | | | + +==============+===========+============+==============+==========+ + | Accept-Query | permanent | List | Section 3 of | | + | | | | RFC 10008 | | + +--------------+-----------+------------+--------------+----------+ + + Table 3: Accept-Query Field Definition + +6. References + +6.1. Normative References + + [HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, + Ed., "HTTP Semantics", STD 97, RFC 9110, + DOI 10.17487/RFC9110, June 2022, + . + + [HTTP-CACHING] + Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, + Ed., "HTTP Caching", STD 98, RFC 9111, + DOI 10.17487/RFC9111, June 2022, + . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + + [STRUCTURED-FIELDS] + Nottingham, M. and P. Kamp, "Structured Field Values for + HTTP", RFC 9651, DOI 10.17487/RFC9651, September 2024, + . + + [URI] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, DOI 10.17487/RFC3986, January 2005, + . + +6.2. Informative References + + [FETCH] WHATWG, "FETCH", WHATWG Living Standard, + . Commit snapshot: + . + + [RFC3253] Clemm, G., Amsden, J., Ellison, T., Kaler, C., and J. + Whitehead, "Versioning Extensions to WebDAV (Web + Distributed Authoring and Versioning)", RFC 3253, + DOI 10.17487/RFC3253, March 2002, + . + + [RFC4918] Dusseault, L., Ed., "HTTP Extensions for Web Distributed + Authoring and Versioning (WebDAV)", RFC 4918, + DOI 10.17487/RFC4918, June 2007, + . + + [RFC5323] Reschke, J., Ed., Reddy, S., Davis, J., and A. Babich, + "Web Distributed Authoring and Versioning (WebDAV) + SEARCH", RFC 5323, DOI 10.17487/RFC5323, November 2008, + . + + [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type + Specifications and Registration Procedures", BCP 13, + RFC 6838, DOI 10.17487/RFC6838, January 2013, + . + + [RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data + Interchange Format", STD 90, RFC 8259, + DOI 10.17487/RFC8259, December 2017, + . + + [RFC9535] Gössner, S., Ed., Normington, G., Ed., and C. Bormann, + Ed., "JSONPath: Query Expressions for JSON", RFC 9535, + DOI 10.17487/RFC9535, February 2024, + . + + [URL] WHATWG, "URL", WHATWG Living Standard, + . Commit snapshot: + . + + [XSLT] Kay, M., Ed., "XSL Transformations (XSLT) Version 3.0", + W3C Recommendation, 8 June 2017, + . + Latest version available at + https://www.w3.org/TR/xslt-30/. + +Appendix A. Examples + + The examples below are for illustrative purposes only; if one needs + to send queries that are actually this short, it is likely better to + use GET. + + The media type used in most examples is "application/x-www-form- + urlencoded" (as used in POST requests from browser user clients, + defined in "application/x-www-form-urlencoded" + (https://url.spec.whatwg.org/#application/x-www-form-urlencoded) in + [URL]). The Content-Length fields have been omitted for brevity. + +A.1. Simple Query + + Below is a simple query with a direct response: + + QUERY /contacts HTTP/1.1 + Host: example.org + Content-Type: application/x-www-form-urlencoded + Accept: application/json + + select=surname,givenname,email&limit=10&match=%22email=*@example.*%22 + + Response: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { "surname": "Smith", + "givenname": "John", + "email": "smith@example.org" }, + { "surname": "Jones", + "givenname": "Sally", + "email": "sally.jones@example.com" }, + { "surname": "Dubois", + "givenname": "Camille", + "email": "camille.dubois@example.net" } + ] + +A.2. Discovery of QUERY Support + + A simple way to discover support for QUERY is provided by the OPTIONS + (Section 9.3.7 of [HTTP]) method: + + OPTIONS /contacts HTTP/1.1 + Host: example.org + + Response: + + HTTP/1.1 200 OK + Allow: GET, QUERY, OPTIONS, HEAD + + The Allow response field (Section 10.2.1 of [HTTP]) denotes the set + of supported methods on the specified resource. + + There are alternatives to the use of OPTIONS. For instance, a QUERY + request can be tried without prior knowledge of server support. The + server would then either process the request, or it could respond + with a 4xx status such as 405 (Method Not Allowed, Section 15.5.6 of + [HTTP]), including the Allow response field. + +A.3. Discovery of QUERY Formats + + The discovery of supported media types for QUERY is possible via the + Accept-Query response field (Section 3): + + HEAD /contacts HTTP/1.1 + Host: example.org + + Response: + + HTTP/1.1 200 OK + Content-Type: application/xhtml + Accept-Query: application/x-www-form-urlencoded, application/sql + + Responses to which request methods will contain Accept-Query will + depend on the resource being accessed. + + An alternative to checking Accept-Query would be to make a QUERY + request, and then -- in case of a 4xx status such as a 415 response + (Unsupported Media Type, Section 15.5.16 of [HTTP]) -- to inspect the + Accept response field (Section 12.5.1 of [HTTP]): + + HTTP/1.1 415 Unsupported Media Type + Content-Type: application/xhtml + Accept: application/x-www-form-urlencoded, application/sql + +A.4. Content-Location, Location, and Indirect Responses + + As described in Sections 2.3 and 2.4, the Content-Location and + Location response fields in success responses (2xx, Section 15.3 of + [HTTP]) provide a way to identify alternate resources that will + respond to GET requests, either for the received result of the + request or for future requests to perform the same operation. Going + back to the example from Appendix A.1: + + QUERY /contacts HTTP/1.1 + Host: example.org + Content-Type: application/x-www-form-urlencoded + Accept: application/json + + select=surname,givenname,email&limit=10&match=%22email=*@example.*%22 + + Response: + + HTTP/1.1 200 OK + Content-Type: application/json + Content-Location: /contacts/stored-results/17 + Location: /contacts/stored-queries/42 + Last-Modified: Sat, 25 Aug 2012 23:34:45 GMT + Date: Sun, 17 Nov 2024, 16:10:24 GMT + + [ + { "surname": "Smith", + "givenname": "John", + "email": "smith@example.org" }, + { "surname": "Jones", + "givenname": "Sally", + "email": "sally.jones@example.com" }, + { "surname": "Dubois", + "givenname": "Camille", + "email": "camille.dubois@example.net" } + ] + +A.4.1. Using Content-Location + + The Content-Location response field received above identifies a + resource holding the result for the QUERY response it appeared on: + + GET /contacts/stored-results/17 HTTP/1.1 + Host: example.org + Accept: application/json + + Response: + + HTTP/1.1 200 OK + Last-Modified: Sat, 25 Aug 2012 23:34:45 GMT + Date: Sun, 17 Nov 2024, 16:10:25 GMT + + [ + { "surname": "Smith", + "givenname": "John", + "email": "smith@example.org" }, + { "surname": "Jones", + "givenname": "Sally", + "email": "sally.jones@example.com" }, + { "surname": "Dubois", + "givenname": "Camille", + "email": "camille.dubois@example.net" } + ] + + Note that there is no guarantee that the server will implement this + resource indefinitely, so, after an error response, the client would + need to redo the original QUERY request in order to obtain a new + alternative location. + +A.4.2. Using Location + + The Location response field identifies a resource that will respond + to GET with a current result for the same process and parameters as + the original QUERY request. + + GET /contacts/stored-queries/42 HTTP/1.1 + Host: example.org + Accept: application/json + + In this example, one entry was removed at 2024-11-17T16:12:01Z (as + indicated in the Last-Modified field), so the response only contains + two entries: + + HTTP/1.1 200 OK + Content-Type: application/json + Last-Modified: Sun, 17 November 2024, 16:12:01 GMT + ETag: "42-1" + Date: Sun, 17 Nov 2024, 16:13:17 GMT + + [ + { "surname": "Smith", + "givenname": "John", + "email": "smith@example.org" }, + { "surname": "Dubois", + "givenname": "Camille", + "email": "camille.dubois@example.net" } + ] + + Assuming that the server still exposes the resource and that there + was no change in the query result, a subsequent conditional GET + request with the following: + + If-None-Match: "42-1" + + would result in a 304 (Not Modified) response (Section 15.4.5 of + [HTTP]). + +A.4.3. Indirect Responses + + Servers can send "indirect" responses (Section 2.5) using the status + code 303 (See Other, Section 15.4.4 of [HTTP]). + + Given the request at the beginning of Appendix A.4, a server might + respond with: + + HTTP/1.1 303 See Other + Content-Type: text/plain + Date: Sun, 17 Nov 2024, 16:13:17 GMT + Location: /contacts/stored-queries/42 + + See stored query at "/contacts/stored-queries/42". + + This is similar to including Location on a direct response, except + that no result for the query is returned. This allows the server to + only generate or reuse an alternative resource. This resource could + then be used as shown in Appendix A.4.2. + +A.5. Conditional Requests + + Consider a resource implementing QUERY that supports "application/ + sql" and "application/xslt+xml" [XSLT] as request media types, and + which can generate responses as "text/csv". The data set being + queried contains RFC document information, and the query returns + information grouped by decade: + + QUERY /rfc-index.xml HTTP/1.1 + Host: example.org + Date: Sun, 7 Sep 2025, 00:00:00 GMT + Content-Type: application/xslt+xml + Accept: text/csv + + ...Query content using XSLT... + + Response: + + HTTP/1.1 200 OK + Date: Sun, 7 Sep 2025, 00:00:00 GMT + Location: /stored-queries/4815162342 + Content-Type: text/csv + Accept-Query: "application/sql", "application/xslt+xml" + Last-Modified: Sun, 31 Aug 2025, 08:44:00 GMT + Vary: Accept-Query, Content-Encoding, Content-Type + + decade, total, with errata, % with errata, average page count + 1960, 26, 5, 19.2, 5.3 + 1970, 666, 18, 2.7, 6.1 + 1980, 376, 44, 11.7, 23.4 + 1990, 1593, 269, 16.9, 25.5 + 2000, 2888, 1048, 36.3, 27.3 + 2010, 2954, 895, 30.3, 26.1 + 2020, 1133, 230, 20.3, 26.2 + + Here, the server has assigned the path "/stored-queries/4815162342" + to the equivalent resource (Section 2.4) for subsequent use with GET. + + Later on, the client repeats the query, but specifies that results + should only be returned when changed: + + QUERY /rfc-index.xml HTTP/1.1 + Host: example.org + Date: Mon, 8, Sep 2025, 11:00:00 GMT + Content-Type: application/sql + Accept: text/csv + If-Modified-Since: Sun, 31 Aug 2025, 08:44:00 GMT + Vary: Accept-Query, Content-Type + + ...Same query, but using SQL... + + The data being queried did not change, therefore the server responds + with: + + HTTP/1.1 304 Not Modified + Date: Mon, 8 Sep 2025, 11:00:00 GMT + Content-Type: text/csv + Location: /stored-queries/4815162342 + Accept-Query: "application/sql", "application/xslt+xml" + Last-Modified: Sun, 31 Aug 2025, 08:44:00 GMT + Vary: Accept-Query, Content-Type + + As the server identified a URI for the equivalent resource, that + resource can be accessed with GET. In particular, this avoids + resending the query request's content: + + GET /stored-queries/4815162342 HTTP/1.1 + Host: example.org + Date: Sun, 21, Sep 2025, 12:08:00 GMT + Accept: text/csv + If-Modified-Since: Sun, 31 Aug 2025, 00:00:00 GMT + + Here, the state of the data set indeed changed, so new content is + returned: + + HTTP/1.1 200 OK + Date: Sun, 21, Sep 2025, 12:08:00 GMT + Content-Type: text/csv + Last-Modified: Thu, 18 Sep 2025, 19:56:00 GMT + Vary: Accept-Query, Content-Encoding, Content-Type + + decade, total, with errata, % with errata, average page count + 1960, 26, 5, 19.2, 5.3 + 1970, 666, 18, 2.7, 6.1 + 1980, 376, 44, 11.7, 23.4 + 1990, 1593, 269, 16.9, 25.5 + 2000, 2888, 1048, 36.3, 27.3 + 2010, 2954, 895, 30.3, 26.1 + 2020, 1133, 230, 20.3, 26.2 + + (Note the change in the row for this decade.) + + The diagrams below illustrate the use of conditional requests and how + they can differ when a URI is assigned to the equivalent resource + (and when the client is taking advantage of it). The fictitious + field name "Validator" is used for demonstration purposes. + + Client Resource + | | + | QUERY with content | + +---------------------------------------->| + | | + | 200 OK | + | Validator: foo | + |<----------------------------------------+ + | | + | QUERY with content | + | (conditional on 'foo') | + +---------------------------------------->| + | | + | 304 Not Modified | + | Validator: foo | + |<----------------------------------------+ + | | + | +--------------+ + | | State Change | + | +--------------+ + | | + | QUERY with content | + | (conditional on 'foo') | + +---------------------------------------->| + | | + | 200 OK | + | Validator: bar | + |<----------------------------------------+ + | | + + Figure 1: Data Flow with QUERY Only + + Client Resource + | | + | QUERY with content | Equivalent Resource + +------------------------------>| (generates /xyz) + | +---------------------------o + | | | + | 200 OK | | + | Validator: foo | | + | Location: /xyz | | + |<------------------------------+ | + | | | + | GET | + | (conditional on 'foo') | + +---------------------------------------------------------->| + | | + | 304 Not Modified | + | Validator: foo | + |<----------------------------------------------------------+ + | | + | +--------------+ + | | State Change | + | +--------------+ + | GET | + | (conditional on 'foo') | + +---------------------------------------------------------->| + | | + | 200 OK | + | Validator: bar | + |<----------------------------------------------------------+ + | | + + Figure 2: Data Flow with GET to Equivalent Resource + +A.6. More Query Formats + + The following examples show requests on a JSON-shaped [RFC8259] + database of RFC errata. + + The request below uses eXtensible Stylesheet Language Transformations + (XSLT) to extract errata information summarized per year and the + defined errata types. + + QUERY /errata.json HTTP/1.1 + Host: example.org + Content-Type: application/xslt+xml + Accept: application/xml, text/csv + + + + + + + + + + errata_status_code + submit_date + + + + + + Response: + + HTTP/1.1 200 OK + Content-Type: text/csv + Accept-Query: "application/jsonpath", "application/xslt+xml" + Date: Wed, 19 Feb 2025, 17:10:01 GMT + + year, total, rejected, verified, hdu, reported + 2000, 14, 0, 14, 0, 0 + 2001, 72, 1, 70, 1, 0 + 2002, 124, 8, 104, 12, 0 + 2003, 63, 0, 61, 2, 0 + 2004, 89, 1, 83, 5, 0 + 2005, 156, 10, 96, 50, 0 + 2006, 444, 54, 176, 214, 0 + 2007, 429, 48, 188, 193, 0 + 2008, 423, 52, 165, 206, 0 + 2009, 331, 39, 148, 144, 0 + 2010, 538, 80, 232, 222, 4 + 2011, 367, 47, 170, 150, 0 + 2012, 348, 54, 149, 145, 0 + 2013, 341, 61, 169, 106, 5 + 2014, 342, 73, 180, 72, 17 + 2015, 343, 79, 145, 89, 30 + 2016, 295, 46, 122, 82, 45 + 2017, 303, 46, 120, 84, 53 + 2018, 350, 61, 118, 98, 73 + 2019, 335, 47, 131, 94, 63 + 2020, 387, 68, 117, 123, 79 + 2021, 321, 44, 148, 63, 66 + 2022, 358, 37, 198, 40, 83 + 2023, 262, 38, 121, 33, 70 + 2024, 322, 33, 125, 23, 141 + 9999, 1, 0, 0, 1, 0 + + Note the Accept-Query response field indicating that another query + format, JSONPath [RFC9535], is supported as well. The request below + would report the identifiers of all rejected errata submitted since + 2024: + + QUERY /errata.json HTTP/1.1 + Host: example.org + Content-Type: application/jsonpath + Accept: application/json + + $..[ + ?@.errata_status_code=="Rejected" + && @.submit_date>"2024" + ] + ["doc-id"] + + Response: + + HTTP/1.1 200 OK + Content-Type: application/json + Accept-Query: "application/jsonpath", "application/xslt+xml" + Date: Thu, 20 Feb 2025, 09:55:42 GMT + Last-Modified: Thu, 20 Feb 2025 06:10:01 GMT + + [ + "RFC1185","RFC8407","RFC6350","RFC8467","RFC1157","RFC9543", + "RFC9076","RFC7656","RFC2822","RFC9460","RFC2104","RFC6797", + "RFC9499","RFC9557","RFC2131","RFC2328","RFC9001","RFC3325", + "RFC9438","RFC2526","RFC2985","RFC7643","RFC9132","RFC6376", + "RFC9110","RFC9460","RFC7748","RFC9497","RFC8463","RFC4035", + "RFC7239","RFC9083","RFC9537","RFC9537","RFC9420","RFC9000", + "RFC9656","RFC9110","RFC2324","RFC2549","RFC6797","RFC2549", + "RFC8894" + ] + +Appendix B. Selection of the Method Name 'QUERY' + + The "Hypertext Transfer Protocol (HTTP) Method Registry" + () already contains + three other methods with the properties "safe" and "idempotent": + "PROPFIND" [RFC4918], "REPORT" [RFC3253], and "SEARCH" [RFC5323]. + + It would have been possible to reuse any of these, updating it in a + way that it matches what this specification defines as the new method + "QUERY". Indeed, the early stages of this specification used + "SEARCH". + + The method name "QUERY" ultimately was chosen because: + + * The alternatives use a generic media type for the request content + ("application/xml"); the semantics of the request depend solely on + the request content. + + * Furthermore, they all originate from the WebDAV activity, about + which many have mixed feelings. + + * "QUERY" captures the relation with the URI's query component well. + +Acknowledgements + + We thank all members of the HTTP Working Group for their ideas, + reviews, and feedback. + + The following individuals deserve special recognition: Carsten + Bormann, Mark Nottingham, Martin Thomson, Michael Thornburgh, Roberto + Polli, Roy Fielding, and Will Hawkins. + +Contributors + + Ashok Malhotra participated in early discussions leading to this + specification: + + Ashok Malhotra + Email: malhotrasahib@gmail.com + + + Discussion on this HTTP method was reopened by Asbjørn Ulsberg during + the HTTP Workshop in 2019: + + Asbjørn Ulsberg + Email: asbjorn@ulsberg.no + URI: https://asbjor.nu/ + + +Authors' Addresses + + Julian Reschke + greenbytes GmbH + Hafenweg 16 + 48155 Münster + Germany + Email: julian.reschke@greenbytes.de + URI: https://greenbytes.de/tech/webdav/ + + + James M Snell + Cloudflare + Email: jasnell@gmail.com + + + Mike Bishop + Akamai + Email: mbishop@evequefou.be From b2a059e73ac0d1956c636b96ff0001d876711abf Mon Sep 17 00:00:00 2001 From: Space One Date: Sat, 11 Jul 2026 13:31:58 +0200 Subject: [PATCH 2/5] ci(ruff): add exceptions for all current violations --- pyproject.toml | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bc7fbae1..40f427d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,6 +80,7 @@ unsafe-fixes = true preview = true select = ["E", "W", "F", "I", "D", "TD", "ALL"] ignore = [ +# Invalid / don't care: "E501", # line-too-long "E203", # whitespace-before-punctuation "COM812", # missing-trailing-comma @@ -102,6 +103,9 @@ ignore = [ "D404", # First word of the docstring should not be "This" "D413", # missing-blank-line-after-last-section "D104", # Missing docstring in public package + "DOC201", # docstring-missing-returns + "DOC402", # docstring-missing-yields + "DOC501", # docstring-missing-exception "EM101", # raw-string-in-exception "ERA001", # commented-out-code "T201", "T203", # print, p-print @@ -109,9 +113,14 @@ ignore = [ "TD002", # missing-todo-author "TD003", # missing-todo-link "RSE102", # unnecessary-paren-on-raise-exception + "UP031", # printf-string-formatting "CPY", # copyright + "EM102", # f-string-in-exception # "I", "S101", "Q000", "ANN", "ERA", "UP004", "ARG001", "ARG002", + "FURB189", # subclass-builtin + "TRY301", # raise-within-try +# too much effort: "ANN001", # missing-type-function-argument "ANN201", # missing-return-type-undocumented-public-function "ANN202", # missing-return-type-private-function @@ -120,6 +129,65 @@ ignore = [ "ANN401", # any-type "ANN204", # missing-return-type-special-method "ANN206", # missing-return-type-class-method + + "ARG001", # unused-function-argument + "ARG003", # unused-class-method-argument + "FIX001", # line-contains-fixme + "FIX002", # line-contains-todo + "PLC2701", # import-private-name + "PLR0904", # too-many-public-methods + "PLR0912", # too-many-branches + +# needs decision: + "PLC2801", # unnecessary-dunder-call + "TRY003", # raise-vanilla-args + "TRY300", # try-consider-else + +# should be fixed! + "A001", # builtin-variable-shadowing + "A003", # builtin-attribute-shadowing + "B015", # useless-comparison + "B018", # useless-expression + "B904", # raise-without-from-inside-except + "D301", # escape-sequence-in-docstring + "DTZ001", # call-datetime-without-tzinfo + "DTZ004", # call-datetime-utcfromtimestamp + "FBT001", # boolean-type-hint-positional-argument + "FBT002", # boolean-default-value-positional-argument + "FBT003", # boolean-positional-value-in-call + "FURB113", # repeated-append + "N802", # invalid-function-name + "N803", # invalid-argument-name + "N805", # invalid-first-argument-name-for-method + "N806", # non-lowercase-variable-in-function + "N818", # error-suffix-on-exception-name + "PERF203", # try-except-in-loop + "PLC0206", # dict-index-missing-items + "PLC0415", # import-outside-top-level + "PLC1901", # compare-to-empty-string + "PLR1704", # redefined-argument-from-local + "PLR2004", # magic-value-comparison + "PLR6201", # literal-membership + "PLR6301", # no-self-use + "PLW0108", # unnecessary-lambda + "PLW0717", # too-many-statements-in-try-clause + "PLW1641", # eq-without-hash + "PLW2901", # redefined-loop-name + "PT011", # pytest-raises-too-broad + "PT012", # pytest-raises-with-multiple-statements + "PYI041", # redundant-numeric-union + "PYI056", # unsupported-method-call-on-all + "RUF005", # collection-literal-concatenation + "RUF012", # mutable-class-default + "RUF022", # unsorted-dunder-all + "RUF039", # unraw-re-pattern + "RUF067", # non-empty-init-module + "RUF069", # float-equality-comparison + "SIM102", # collapsible-if + "SIM108", # if-else-block-instead-of-if-exp + "SIM115", # open-file-with-context-handler + "RUF100", # unused-noqa + "S101", # assert ] @@ -154,7 +222,12 @@ task-tags = ["TODO", "FIXME"] [tool.ruff.lint.extend-per-file-ignores] "tests/*" = [ "ANN", - "S101", + "S101", # assert + "S105", # hardcoded-password-string + "S108", # hardcoded-temp-file + "S404", # suspicious-subprocess-import + "S405", # suspicious-xml-etree-import + "S603", # subprocess-without-shell-equals-true ] "httoop/status/*.py" = [ "N801", From e1cfdcb965dc7a9d5124974ef40ef78e55e60437 Mon Sep 17 00:00:00 2001 From: Space One Date: Sat, 11 Jul 2026 03:17:48 +0200 Subject: [PATCH 3/5] style: todo-tags --- tests/authentication/test_httpauth.py | 2 +- tests/headers/test_host_header.py | 4 ++-- tests/headers/test_security_headers.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/authentication/test_httpauth.py b/tests/authentication/test_httpauth.py index 12f36716..04da797e 100644 --- a/tests/authentication/test_httpauth.py +++ b/tests/authentication/test_httpauth.py @@ -152,7 +152,7 @@ def test_multibasicunknown2(authentication): @pytest.mark.xfail(reason='FIXME') -def test_multibasicunknown2mf(authentication): # TODO +def test_multibasicunknown2mf(authentication): # TODO: fix a = authentication(b'WWW-Authenticate: Newauth realm="newauth"') assert a a = authentication(b'WWW-Authenticate: Basic realm="basic"') diff --git a/tests/headers/test_host_header.py b/tests/headers/test_host_header.py index f14216ba..906799b6 100644 --- a/tests/headers/test_host_header.py +++ b/tests/headers/test_host_header.py @@ -58,8 +58,8 @@ def _test_iter(header, host, port, headers): @pytest.mark.parametrize('invalid', list( list((set('\x7F()<>@,;:/\\[\\]={} \t\\\\^"\'') | set(map(chr, range(0x1F)))) - set(';\x00')) + [ ', ', - pytest.param(';', marks=pytest.mark.xfail), # FIXME - pytest.param('\x00', marks=pytest.mark.xfail), # FIXME + pytest.param(';', marks=pytest.mark.xfail), # FIXME: fails + pytest.param('\x00', marks=pytest.mark.xfail), # FIXME: fails ] )) def test_invalid_host_header(invalid, headers): diff --git a/tests/headers/test_security_headers.py b/tests/headers/test_security_headers.py index fc1059dc..78f3a4d8 100644 --- a/tests/headers/test_security_headers.py +++ b/tests/headers/test_security_headers.py @@ -3,7 +3,7 @@ def test_content_security_policy(headers): assert bytes(headers.get_element('Content-Security-Policy', 'media-src')) == b"media-src 'self'; " -def test_content_security_policy_element(headers): # TODO(FIXME: very bad API design +def test_content_security_policy_element(headers): # FIXME: very bad API design headers.append_element('Content-Security-Policy', 'default-src', {b"'self'": None}) headers.append_element('Content-Security-Policy', 'font-src', {b"'none'": None}) assert bytes(headers) == b"Content-Security-Policy: default-src 'self'; , font-src 'none'; \r\n\r\n" From fcbba3cd46e6182a4a19453dd1e5c203624798d7 Mon Sep 17 00:00:00 2001 From: Space One Date: Sat, 11 Jul 2026 13:34:00 +0200 Subject: [PATCH 4/5] style: address ruff violations --- httoop/codecs/application/xml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httoop/codecs/application/xml.py b/httoop/codecs/application/xml.py index 405f047e..f11983d4 100644 --- a/httoop/codecs/application/xml.py +++ b/httoop/codecs/application/xml.py @@ -15,7 +15,7 @@ from defusedxml.ElementTree import ParseError, fromstring, tostring except ImportError: # pragma: no cover # TODO: emit a warning - from xml.etree.ElementTree import ParseError, tostring + from xml.etree.ElementTree import ParseError, tostring # noqa: S405 def fromstring(data) -> NoReturn: raise ParseError('Will not parse without defusedxml!') From 10c7d186601a7d6f015de9f70b1a8aed67615d89 Mon Sep 17 00:00:00 2001 From: Space One Date: Sat, 11 Jul 2026 02:57:42 +0200 Subject: [PATCH 5/5] ci(pre-commit): update versions --- .pre-commit-config.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f68d2a82..c7a60d29 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: - --autofix - --no-ensure-ascii - id: trailing-whitespace + exclude: "^rfc/.*[.]txt$|^tests/tc2231/tc2231.html$" - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: @@ -24,7 +25,7 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.14 + rev: v0.15.21 hooks: - id: ruff-check alias: "ruff" @@ -54,7 +55,7 @@ repos: stages: [commit-msg] args: ["--strict"] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v2.1.0 + rev: v2.2.0 hooks: - id: mypy stages: [manual]