Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[apollo-server-koa] sending wrong content-type returns 500 without description #1841

Closed
selaux opened this issue Oct 22, 2018 · 6 comments
Closed
Labels
🪲 bug 🌹 has-reproduction ❤ Has a reproduction in Glitch, CodeSandbox or Git repository.

Comments

@selaux
Copy link

selaux commented Oct 22, 2018

Expected Behavior:
Requesting the graphql endpoint using POST and a content-type header that is not application/json or maybe application/graphql should result in a 400 Bad Request or 415 Unsupported Media Type response from the server with a meaningful response in the body.

Current Behavior:
Requesting the POST endpoint with e.g. content-type header text/plain results in a 500 Internal Server Error response with the following body: {}. Imho this is semantically incorrect (the client did the mistake, not the sever) and not helpful (no explanation of the error at all).

Details:
I looked into this a bit and I cant find where the error is coming from. The request goes into the bodyParser middleware, which resolves with {}. Then the subsequent middleware is not called anymore and I don't know why...

Steps to reproduce:

Install:
npm i [email protected] [email protected] [email protected]

Run:

const Koa = require('koa');
const { ApolloServer } = require('apollo-server-koa');
const supertest = require('supertest');

const RootQuery = 'type RootQuery { ping: String!, pingUserId: String! }';
const SchemaDefinition = 'schema { query: RootQuery }';
const resolvers = {
    RootQuery: {
        ping() {
            return 'pong';
        }
    }
};
const graphqlServerOptions = { typeDefs: [ SchemaDefinition, RootQuery ], resolvers };

const app = new Koa();
const graphqlServer = new ApolloServer(graphqlServerOptions);

graphqlServer.applyMiddleware({
    app,
    path: '/api'
});


async function runExample() {
    const contentType = 'text/plain';
    const server = app.listen();
    const request = supertest(server);
    const response = await request.post('/api')
        .set('Content-Type', contentType)
        .send(JSON.stringify({ query: '{ping}' }));

    console.log(response.statusCode);
    console.log(response.body);

    server.close();
}

runExample();
@selaux selaux changed the title apollo-server-koa: sending wrong content-type returns 500 without description [apollo-server-koa] sending wrong content-type returns 500 without description Oct 22, 2018
@ghost ghost added the 🌹 has-reproduction ❤ Has a reproduction in Glitch, CodeSandbox or Git repository. label Oct 22, 2018
@kratos-42
Copy link

So... any news about this?

@abernix
Copy link
Member

abernix commented Jul 2, 2019

Runnable reproduction of the above:

Edit Apollo Server

@nihalgonsalves
Copy link

This also applies to the default server setup without using any specific webserver (though this means that it's using Express under the hood).

It looks like you receive a 500 unless you have both a valid Content-Type & valid JSON body (an invalid Content-Type has the same effect as not providing the header entirely).

Here are some examples against the default sandbox. I would expect all of these to be 400 errors, and even though the invalid JSON body returns a 400, I would expect that it returns a JSON error, and not an HTML page exposing a stacktrace.

🚫 No Content-Type; 🚫 No body:

$ curl -X POST -sD - https://apollo-server.sse.codesandbox.io/graphql
HTTP/2 500 
server: nginx/1.15.9
date: Tue, 27 Aug 2019 11:14:14 GMT
x-powered-by: Express
access-control-allow-origin: *
strict-transport-security: max-age=15724800; includeSubDomains

POST body missing. Did you forget use body-parser middleware?

✅ Valid Content-Type; 🚫 No body:

HTTP/2 500 
server: nginx/1.15.9
date: Tue, 27 Aug 2019 11:15:56 GMT
x-powered-by: Express
access-control-allow-origin: *
strict-transport-security: max-age=15724800; includeSubDomains

POST body missing. Did you forget use body-parser middleware?

🚫 No Content-Type; ✅ Valid body:

$ curl -X POST -sD - https://apollo-server.sse.codesandbox.io/graphql -d '{ "query": ""}'
HTTP/2 500 
server: nginx/1.15.9
date: Tue, 27 Aug 2019 11:16:38 GMT
x-powered-by: Express
access-control-allow-origin: *
strict-transport-security: max-age=15724800; includeSubDomains

POST body missing. Did you forget use body-parser middleware?

✅ Valid Content-Type; ✅ Valid body:

$ curl -X POST -sD - https://apollo-server.sse.codesandbox.io/graphql -H 'Content-Type: application/json' -d '{ "query": ""}'
HTTP/2 400 
server: nginx/1.15.9
date: Tue, 27 Aug 2019 11:17:34 GMT
x-powered-by: Express
access-control-allow-origin: *
strict-transport-security: max-age=15724800; includeSubDomains

Must provide query string.

✅ Valid Content-Type; 🚫 Malformed body:

$ curl -X POST -sD - https://apollo-server.sse.codesandbox.io/graphql -H 'Content-Type: application/json' -d 'x'
HTTP/2 400 
server: nginx/1.15.9
date: Tue, 27 Aug 2019 11:20:02 GMT
content-type: text/html; charset=utf-8
x-powered-by: Express
access-control-allow-origin: *
content-security-policy: default-src 'self'
x-content-type-options: nosniff
strict-transport-security: max-age=15724800; includeSubDomains

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>SyntaxError: Unexpected token x in JSON at position 0<br> &nbsp; &nbsp;at JSON.parse (&lt;anonymous&gt;)<br> &nbsp; &nbsp;at createStrictSyntaxError (/sandbox/node_modules/apollo-server-express/node_modules/body-parser/lib/types/json.js:158:10)<br> &nbsp; &nbsp;at parse (/sandbox/node_modules/apollo-server-express/node_modules/body-parser/lib/types/json.js:83:15)<br> &nbsp; &nbsp;at /sandbox/node_modules/apollo-server-express/node_modules/body-parser/lib/read.js:121:18<br> &nbsp; &nbsp;at invokeCallback (/sandbox/node_modules/apollo-server-express/node_modules/raw-body/index.js:224:16)<br> &nbsp; &nbsp;at done (/sandbox/node_modules/apollo-server-express/node_modules/raw-body/index.js:213:7)<br> &nbsp; &nbsp;at IncomingMessage.onEnd (/sandbox/node_modules/apollo-server-express/node_modules/raw-body/index.js:273:7)<br> &nbsp; &nbsp;at IncomingMessage.emit (events.js:203:15)<br> &nbsp; &nbsp;at endReadableNT (_stream_readable.js:1129:12)<br> &nbsp; &nbsp;at process._tickCallback (internal/process/next_tick.js:63:19)</pre>
<script src="https://sse.codesandbox.io/client-hook.js"></script></body>
</html>

@abernix abernix added this to the Release 3.x milestone Aug 28, 2019
@abernix
Copy link
Member

abernix commented Aug 28, 2019

@nihalgonsalves Thank you for providing the research you have here. I've tagged this for the Apollo Server 3.x release since I think it highlights some problematic patterns with the Apollo Server 2.x series that we'll need a major version bump to resolve safely.

And specifically, I think any resolution should be built into the transport, which I've proposed in #3184, so as to be universally resolved in all frameworks (e.g. Koa, Express, etc.)

@glasser
Copy link
Member

glasser commented May 2, 2021

We're no longer planning to resolve #3184 in AS3. I will look into how reasonable it is to resolve this issue specifically in the context of AS3 vs waiting for a release with #3184.

@glasser glasser removed this from the Release 3.x milestone Jun 10, 2021
glasser added a commit that referenced this issue Jun 15, 2021
There are a lot of reasons that a supposedly GraphQL request might not
make it to requestPipeline: missing body, bad JSON, bad content-type,
etc. Previously, many of these outcomes resulted in 5xx errors despite
them being client errors (which should be 4xx), and their handling was
pretty inconsistent across the integrations.

Part of the confusion was a particular error message which combined "you
seem to have set up your server wrong by not setting up body parsing
properly" with "there was a problem parsing this request". The former is
reasonable to be 500 but the latter is much more likely to be what's
actually happening (you have to actively disable body parsing to make
that happen!). And the recommendation was ungrammatical and
express-specific despite being in apollo-server-core. But the good news
is that for some reason the Express body-parser unconditionally sets
`req.body` to at least `{}` before checking `content-type` (see
https://github.com/expressjs/body-parser/blob/480b1cfe29af19c070f4ae96e0d598c099f42a12/lib/types/json.js#L105-L117)
so we can move the "500 if body not parsed" from `apollo-server-core` to
`apollo-server-express` and directly detect whether `req.body` is empty.

This is a backwards-incompatible change, so we can finally implement it
as v3 is imminent.

The PR consists of:

- Core
  - Make an error about needing a query be clear that it needs to be a
    non-empty query (the error could fire before if `query` was provided
    yet empty).
  - Make "POST body missing" error 400 instead of 500, include that it
    could be bad content-type or empty JSON object, and don't suggest
    (with poor grammar) the use of body-parser (as described above).
    Also show this error if httpRequest.query is a string or Buffer
    (this can happen with Azure Functions on JSON parse failures).
- Express: Check for a sign that body-parser hasn't been applied here
  instead of in Core and provide a more targeted body-parser-suggesting
  500.
- Micro: Only parse JSON for `content-type: application/json` (like all
  other integrations).
- Azure Functions
  - 400 instead of 500 for missing body
  - Make test more realistic on parser failures; see Core change for
    where this matters.

Fixes #1841 (thanks to @nihalgonsalves for a good set of test cases).
Fixes #4165.
@glasser
Copy link
Member

glasser commented Jun 15, 2021

Fixed on release-3.0 by #5305.

@glasser glasser closed this as completed Jun 15, 2021
glasser added a commit that referenced this issue Jun 15, 2021
There are a lot of reasons that a supposedly GraphQL request might not
make it to requestPipeline: missing body, bad JSON, bad content-type,
etc. Previously, many of these outcomes resulted in 5xx errors despite
them being client errors (which should be 4xx), and their handling was
pretty inconsistent across the integrations.

Part of the confusion was a particular error message which combined "you
seem to have set up your server wrong by not setting up body parsing
properly" with "there was a problem parsing this request". The former is
reasonable to be 500 but the latter is much more likely to be what's
actually happening (you have to actively disable body parsing to make
that happen!). And the recommendation was ungrammatical and
express-specific despite being in apollo-server-core. But the good news
is that for some reason the Express body-parser unconditionally sets
`req.body` to at least `{}` before checking `content-type` (see
https://github.com/expressjs/body-parser/blob/480b1cfe29af19c070f4ae96e0d598c099f42a12/lib/types/json.js#L105-L117)
so we can move the "500 if body not parsed" from `apollo-server-core` to
`apollo-server-express` and directly detect whether `req.body` is empty.

This is a backwards-incompatible change, so we can finally implement it
as v3 is imminent.

The PR consists of:

- Core
  - Make an error about needing a query be clear that it needs to be a
    non-empty query (the error could fire before if `query` was provided
    yet empty).
  - Make "POST body missing" error 400 instead of 500, include that it
    could be bad content-type or empty JSON object, and don't suggest
    (with poor grammar) the use of body-parser (as described above).
    Also show this error if httpRequest.query is a string or Buffer
    (this can happen with Azure Functions on JSON parse failures).
- Express: Check for a sign that body-parser hasn't been applied here
  instead of in Core and provide a more targeted body-parser-suggesting
  500.
- Micro: Only parse JSON for `content-type: application/json` (like all
  other integrations).
- Azure Functions
  - 400 instead of 500 for missing body
  - Make test more realistic on parser failures; see Core change for
    where this matters.

Fixes #1841 (thanks to @nihalgonsalves for a good set of test cases).
Fixes #4165.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🪲 bug 🌹 has-reproduction ❤ Has a reproduction in Glitch, CodeSandbox or Git repository.
Projects
None yet
Development

No branches or pull requests

5 participants