Skip to content

Serve GraphQL over HTTP from greem::http and audit it with graphql-http - #47

Merged
XiNiHa merged 2 commits into
mainfrom
http-audit
Oct 16, 2026
Merged

XiNiHa merged 2 commits into
mainfrom
http-audit

Conversation

@XiNiHa

@XiNiHa XiNiHa commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

greem::http::handle owns the whole GraphQL-over-HTTP exchange that the axum example and greem-bench's serve each used to hand-write. CI then audits it with graphql-http.

handle(schema, roots, ctx, RequestParts { method, query_string, content_type, accept, body }, options)
  method            GET | POST | QUERY, else 405 Allow: GET, POST, QUERY
  negotiate(Accept) graphql-response+json | json | multipart (q-values), else 406
  request(method)   GET query string | POST, QUERY application/json (415 / 400 / 422)
  schema.operation  RequestErrorKind::Parse → 400, any other kind → 422
  GET + mutation    405 Allow: POST
  QUERY + mutation  422
  execute_stream    first payload has hasNext, and multipart q ≥ JSON's → multipart 200
                    multipart-only Accept → even a single result as one part, 200
                    else 200, or 294 when data comes with errors

Status codes follow the GraphQL-over-HTTP draft, not graphql-http's audits where the two disagree (ADR-0012). A legacy application/json client gets the same codes, and only a 2xx keeps application/json. QUERY (RFC 10008) is served like POST but only for queries, as graphql-over-http#411 proposes ahead of the draft, whose "MAY support other methods" already allows it.

 greem/src/
-├── http.rs
+├── http/
+│   ├── mod.rs        # handle, Request, multipart_part
+│   ├── accept.rs     # Accept negotiation
+│   ├── request.rs    # RequestParts, GET/POST/QUERY parsing, well-formedness
+│   ├── response.rs   # Response, Body, status per payload
+│   └── convert.rs    # `http` feature: http::request::Parts in, http::Response<Body> out
+http-audit/           # graphql-http 1.23.1 audits + overrides.json (root pnpm workspace)
+.github/workflows/http-audit.yml          # audit the example, job summary, artifact
+.github/workflows/http-audit-comment.yml  # workflow_run: sticky PR comment, forks included

The http feature is opt-in. actix-web sits on http 0.2, so the core takes plain &str/&[u8] parts. bench/ becomes its own pnpm workspace, and mise.toml now pins pnpm.

Evidence

  • Before: graphql-http 1.23.1 against examples/axum had 0 errors, 4 warnings (22EB, 572B, FDE2, 7B9B) and 15 notices. GET wasn't served (5A70, D6D5, 6A70), and nothing ever answered application/graphql-response+json.
    After: 39 pass, 0 errors, and 4 warnings, all overridden as draft conflicts:

    Audit graphql-http expects greem, per the draft
    572B, FDE2 200 for a parse failure under application/json 400
    7B9B 200 for a coercion failure under application/json 422
    86EE 400 for a coercion failure under graphql-response+json 422

    The 18 notices are the MAY-level "400 for malformed parameters" audits; greem answers 422, as the draft says. 74FF passes, because its "validation failure" document doesn't even lex.

  • Over a real socket, curl -X QUERY against the example gets 200 for a query and 422 for a mutation, and PUT gets 405 Allow: GET, POST, QUERY. graphql-http has no QUERY audits, so the audit result is unchanged.

  • The gate fails as it should: with 86EE removed from the overrides and the passing 74FF added, the audit exits 1 and reports both problems.

  • cargo test -p greem --all-features --test http: 13 cases covering every status path, plus http round-trips. The example and serve tests, workspace clippy, docs, MSRV 1.96, and the bench/ workspace build all pass.

Merge Danger

Door: two-way

The comment workflow only runs from main (workflow_run), so this PR shows the report in the job summary; the first sticky comment will appear on the next PR.

Blast Radius: API

  • These are breaking changes to greem's public API: RequestErrors becomes { kind, errors }, PayloadKind::RequestError carries a RequestErrorKind, and greem::http::accepts_multipart is gone.
  • Existing HTTP clients of the example see new status codes. Validation errors go from 400 to 422, and partial success is 294, an unregistered 2xx code that intermediaries which allow only known codes may reject.
  • JSON responses now include ; charset=utf-8 in their content type.
  • QUERY follows graphql-over-http#411, which is still open. If it lands differently, for example with 405 for a mutation, handle needs a follow-up.

Closes #21

🤖 Generated with Claude Code

@codspeed

codspeed Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing http-audit (51d3f27) with main (b5403a6)

Open in CodSpeed

@XiNiHa

XiNiHa commented Oct 16, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T06:42:27.251996Z 6950f1f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6950f1f27b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread greem/src/http/accept.rs Outdated
Comment thread greem/src/http/accept.rs Outdated
Comment thread greem/src/http/convert.rs
XiNiHa and others added 2 commits October 16, 2026 16:21
greem::http::handle takes the parts of an HTTP request (method, query
string, Content-Type, Accept and the body) and answers a Response. It
serves GET, and POST and QUERY with an application/json body, and
negotiates Accept with q-values between
application/graphql-response+json, legacy application/json and
multipart/mixed. Status codes follow the GraphQL-over-HTTP draft: 400
for a body or document that does not parse, 422 for other request errors
and for malformed parameters, 294 for data alongside errors, 405 for a
mutation over GET, and 406 and 415 for media types it cannot serve.
QUERY (RFC 10008) runs queries only and answers a mutation with 422, as
graphql/graphql-over-http#411 proposes ahead of the draft. Request
errors carry a RequestErrorKind, so the status follows the step that
failed. The http feature converts from http::request::Parts and into an
http::Response whose body is an http_body::Body.

The axum example and greem-bench's serve binary now hand each request to
it instead of keeping their own copies of the same handler. ADR-0012
records following the draft where graphql-http's audits disagree.

Closes #21

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
http-audit/ runs graphql-http 1.23.1's server audits against a URL and
fails on a MUST error, on a SHOULD warning that overrides.json does not
name, and on an override whose audit no longer warns. The four overrides
are where graphql-http contradicts the GraphQL-over-HTTP draft greem
follows: 200 for request errors under application/json, and 400 for
variable coercion failures. Each one cites the draft section it
contradicts.

http-audit.yml starts the axum example, audits it, writes the report to
the job summary and uploads it. http-audit-comment.yml runs on that
workflow's completion from the default branch, so it has a write token
even for forks' pull requests. It posts the report as one comment per
pull request, updated in place, and finds the pull request from the
event, not from the artifact.

The repository root becomes a pnpm workspace holding http-audit, and
bench/ becomes its own workspace with the graphql-js server's lockfile.
mise.toml pins pnpm beside Node.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@XiNiHa
XiNiHa merged commit 51d3f27 into main Oct 16, 2026
8 checks passed
@XiNiHa
XiNiHa deleted the http-audit branch October 16, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP response helper: own the single-vs-multipart decision in greem::http

1 participant