Browse Source

Send error messages in the response body, not the status line

The HTTP status line message is not intended for arbitrary messages and
not suitable for freeform text due to character restrictions.

Using statusMessage this way has caused nextstrain.org server crashes
when the field is assigned an error containing a newline, as newlines
are not allowed.

Resolves #1180.
master
Thomas Sibley 2 years ago
parent
commit
9bcf200451
  1. 3
      cli/server/getDatasetHelpers.js
  2. 6
      cli/server/getNarrative.js
  3. 6
      cli/view.js
  4. 6
      docs-src/docs/server/api.md
  5. 8
      docs/server/api.html
  6. 8
      docs/server/api/index.html
  7. 25
      src/actions/loadData.js
  8. 22
      src/util/serverInteraction.js

3
cli/server/getDatasetHelpers.js

@ -15,9 +15,8 @@ const convertFromV1 = require("./convertJsonSchemas").convertFromV1;
const fs = require("fs");
const handleError = (res, clientMsg, serverMsg="") => {
res.statusMessage = clientMsg;
utils.warn(`${clientMsg} -- ${serverMsg}`);
return res.status(500).end();
return res.status(500).type("text/plain").send(clientMsg);
};
const splitPrefixIntoParts = (url) => url

6
cli/server/getNarrative.js

@ -21,9 +21,9 @@ const setUpGetNarrativeHandler = ({narrativesPath}) => {
res.send(JSON.stringify(blocks).replace(/</g, '\\u003c'));
utils.verbose("SUCCESS");
} catch (err) {
res.statusMessage = `Narratives couldn't be served -- ${err.message}`;
utils.warn(res.statusMessage);
res.status(500).end();
const errorMessage = `Narratives couldn't be served -- ${err.message}`;
utils.warn(errorMessage);
res.status(500).type("text/plain").send(errorMessage);
}
};
};

6
cli/view.js

@ -65,9 +65,9 @@ const loadAndAddHandlers = ({app, handlersArg, datasetDir, narrativeDir}) => {
app.get("/charon/getDataset", handlers.getDataset);
app.get("/charon/getNarrative", handlers.getNarrative);
app.get("/charon*", (req, res) => {
res.statusMessage = "Query unhandled -- " + req.originalUrl;
utils.warn(res.statusMessage);
return res.status(500).end();
const errorMessage = "Query unhandled -- " + req.originalUrl;
utils.warn(errorMessage);
return res.status(500).type("text/plain").send(errorMessage);
});
return handlersArg ?

6
docs-src/docs/server/api.md

@ -110,9 +110,9 @@ const getAvailable = (req, res) => {
/* collect available data */
res.json(data);
} catch (err) {
res.statusMessage = `error message to display in client`;
console.log(res.statusMessage); /* printed by the server, not the client */
return res.status(500).end();
const errorMessage = `error message to display in client`;
console.log(errorMessage); /* printed by the server, not the client */
return res.status(500).type("text/plain").send(errorMessage);
}
};
```

8
docs/server/api.html

@ -139,9 +139,9 @@ The provided JavaScript file must export three functions, each of which handles
<span class="hljs-comment">/* collect available data */</span>
res.json(data);
} <span class="hljs-keyword">catch</span> (err) {
res.statusMessage = <span class="hljs-string">`error message to display in client`</span>;
<span class="hljs-built_in">console</span>.log(res.statusMessage); <span class="hljs-comment">/* printed by the server, not the client */</span>
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).end();
<span class="hljs-keyword">const</span> errorMessage = <span class="hljs-string">`error message to display in client`</span>;
<span class="hljs-built_in">console</span>.log(errorMessage); <span class="hljs-comment">/* printed by the server, not the client */</span>
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).type(<span class="hljs-string">"text/plain"</span>).send(errorMessage);
}
};
</code></pre>
@ -173,4 +173,4 @@ Each object has properties</p>
<li><code>dataset</code> -- the dataset associated with this block</li>
<li><code>query</code> -- the query associated with this block</li>
</ul>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 22/03/2020</em></div><div class="docs-prevnext"><a class="docs-prev button" href="/auspice/server/introduction"><span class="arrow-prev"></span><span>Auspice servers</span></a><a class="docs-next button" href="/auspice/server/authentication"><span>Authentication</span><span class="arrow-next"></span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#auspice-client-requests">Auspice client requests</a><ul class="toc-headings"><li><a href="#charon-getavailable"><code>/charon/getAvailable</code></a></li><li><a href="#charon-getdataset"><code>/charon/getDataset</code></a></li><li><a href="#charon-getnarrative"><code>/charon/getNarrative</code></a></li></ul></li><li><a href="#suppling-custom-handlers-to-the-auspice-server">Suppling custom handlers to the Auspice server</a></li><li><a href="#importing-code-from-auspice">Importing code from Auspice</a><ul class="toc-headings"><li><a href="#convertfromv1"><code>convertFromV1</code></a></li><li><a href="#parsenarrativefile"><code>parseNarrativeFile</code></a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><div><a href="/auspice/"><img style="padding-left:20px" src="/auspice/img/logo-light.svg" alt="Auspice" width="66" height="58"/></a></div><div><h5>External Links</h5><a href="https://github.com/nextstrain/auspice">GitHub repo</a><a href="https://www.npmjs.com/package/auspice">NPM package</a><a href="https://nextstrain.org">Nextstrain</a></div><div><h5>Contact Us</h5><a href="mailto:hello@nextstrain.org">email</a><a href="https://twitter.com/hamesjadfield">twitter</a></div></section><section class="copyright">Website built by <a href="https://twitter.com/hamesjadfield">James Hadfield</a> using <a href="https://docusaurus.io">Docusaurus</a></section><section class="copyright">If you use auspice, please cite <a href="https://doi.org/10.1093/bioinformatics/bty407">Hadfield et al., 2018</a></section><section class="copyright">Copyright © 2014-2020 Richard Neher &amp; Trevor Bedford</section></footer></div></body></html>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 3/21/2020</em></div><div class="docs-prevnext"><a class="docs-prev button" href="/auspice/server/introduction"><span class="arrow-prev"></span><span>Auspice servers</span></a><a class="docs-next button" href="/auspice/server/authentication"><span>Authentication</span><span class="arrow-next"></span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#auspice-client-requests">Auspice client requests</a><ul class="toc-headings"><li><a href="#charon-getavailable"><code>/charon/getAvailable</code></a></li><li><a href="#charon-getdataset"><code>/charon/getDataset</code></a></li><li><a href="#charon-getnarrative"><code>/charon/getNarrative</code></a></li></ul></li><li><a href="#suppling-custom-handlers-to-the-auspice-server">Suppling custom handlers to the Auspice server</a></li><li><a href="#importing-code-from-auspice">Importing code from Auspice</a><ul class="toc-headings"><li><a href="#convertfromv1"><code>convertFromV1</code></a></li><li><a href="#parsenarrativefile"><code>parseNarrativeFile</code></a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><div><a href="/auspice/"><img style="padding-left:20px" src="/auspice/img/logo-light.svg" alt="Auspice" width="66" height="58"/></a></div><div><h5>External Links</h5><a href="https://github.com/nextstrain/auspice">GitHub repo</a><a href="https://www.npmjs.com/package/auspice">NPM package</a><a href="https://nextstrain.org">Nextstrain</a></div><div><h5>Contact Us</h5><a href="mailto:hello@nextstrain.org">email</a><a href="https://twitter.com/hamesjadfield">twitter</a></div></section><section class="copyright">Website built by <a href="https://twitter.com/hamesjadfield">James Hadfield</a> using <a href="https://docusaurus.io">Docusaurus</a></section><section class="copyright">If you use auspice, please cite <a href="https://doi.org/10.1093/bioinformatics/bty407">Hadfield et al., 2018</a></section><section class="copyright">Copyright © 2014-2020 Richard Neher &amp; Trevor Bedford</section></footer></div></body></html>

8
docs/server/api/index.html

@ -139,9 +139,9 @@ The provided JavaScript file must export three functions, each of which handles
<span class="hljs-comment">/* collect available data */</span>
res.json(data);
} <span class="hljs-keyword">catch</span> (err) {
res.statusMessage = <span class="hljs-string">`error message to display in client`</span>;
<span class="hljs-built_in">console</span>.log(res.statusMessage); <span class="hljs-comment">/* printed by the server, not the client */</span>
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).end();
<span class="hljs-keyword">const</span> errorMessage = <span class="hljs-string">`error message to display in client`</span>;
<span class="hljs-built_in">console</span>.log(errorMessage); <span class="hljs-comment">/* printed by the server, not the client */</span>
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).type(<span class="hljs-string">"text/plain"</span>).send(errorMessage);
}
};
</code></pre>
@ -173,4 +173,4 @@ Each object has properties</p>
<li><code>dataset</code> -- the dataset associated with this block</li>
<li><code>query</code> -- the query associated with this block</li>
</ul>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 22/03/2020</em></div><div class="docs-prevnext"><a class="docs-prev button" href="/auspice/server/introduction"><span class="arrow-prev"></span><span>Auspice servers</span></a><a class="docs-next button" href="/auspice/server/authentication"><span>Authentication</span><span class="arrow-next"></span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#auspice-client-requests">Auspice client requests</a><ul class="toc-headings"><li><a href="#charon-getavailable"><code>/charon/getAvailable</code></a></li><li><a href="#charon-getdataset"><code>/charon/getDataset</code></a></li><li><a href="#charon-getnarrative"><code>/charon/getNarrative</code></a></li></ul></li><li><a href="#suppling-custom-handlers-to-the-auspice-server">Suppling custom handlers to the Auspice server</a></li><li><a href="#importing-code-from-auspice">Importing code from Auspice</a><ul class="toc-headings"><li><a href="#convertfromv1"><code>convertFromV1</code></a></li><li><a href="#parsenarrativefile"><code>parseNarrativeFile</code></a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><div><a href="/auspice/"><img style="padding-left:20px" src="/auspice/img/logo-light.svg" alt="Auspice" width="66" height="58"/></a></div><div><h5>External Links</h5><a href="https://github.com/nextstrain/auspice">GitHub repo</a><a href="https://www.npmjs.com/package/auspice">NPM package</a><a href="https://nextstrain.org">Nextstrain</a></div><div><h5>Contact Us</h5><a href="mailto:hello@nextstrain.org">email</a><a href="https://twitter.com/hamesjadfield">twitter</a></div></section><section class="copyright">Website built by <a href="https://twitter.com/hamesjadfield">James Hadfield</a> using <a href="https://docusaurus.io">Docusaurus</a></section><section class="copyright">If you use auspice, please cite <a href="https://doi.org/10.1093/bioinformatics/bty407">Hadfield et al., 2018</a></section><section class="copyright">Copyright © 2014-2020 Richard Neher &amp; Trevor Bedford</section></footer></div></body></html>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 3/21/2020</em></div><div class="docs-prevnext"><a class="docs-prev button" href="/auspice/server/introduction"><span class="arrow-prev"></span><span>Auspice servers</span></a><a class="docs-next button" href="/auspice/server/authentication"><span>Authentication</span><span class="arrow-next"></span></a></div></div></div><nav class="onPageNav"><ul class="toc-headings"><li><a href="#auspice-client-requests">Auspice client requests</a><ul class="toc-headings"><li><a href="#charon-getavailable"><code>/charon/getAvailable</code></a></li><li><a href="#charon-getdataset"><code>/charon/getDataset</code></a></li><li><a href="#charon-getnarrative"><code>/charon/getNarrative</code></a></li></ul></li><li><a href="#suppling-custom-handlers-to-the-auspice-server">Suppling custom handlers to the Auspice server</a></li><li><a href="#importing-code-from-auspice">Importing code from Auspice</a><ul class="toc-headings"><li><a href="#convertfromv1"><code>convertFromV1</code></a></li><li><a href="#parsenarrativefile"><code>parseNarrativeFile</code></a></li></ul></li></ul></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><div><a href="/auspice/"><img style="padding-left:20px" src="/auspice/img/logo-light.svg" alt="Auspice" width="66" height="58"/></a></div><div><h5>External Links</h5><a href="https://github.com/nextstrain/auspice">GitHub repo</a><a href="https://www.npmjs.com/package/auspice">NPM package</a><a href="https://nextstrain.org">Nextstrain</a></div><div><h5>Contact Us</h5><a href="mailto:hello@nextstrain.org">email</a><a href="https://twitter.com/hamesjadfield">twitter</a></div></section><section class="copyright">Website built by <a href="https://twitter.com/hamesjadfield">James Hadfield</a> using <a href="https://docusaurus.io">Docusaurus</a></section><section class="copyright">If you use auspice, please cite <a href="https://doi.org/10.1093/bioinformatics/bty407">Hadfield et al., 2018</a></section><section class="copyright">Copyright © 2014-2020 Richard Neher &amp; Trevor Bedford</section></footer></div></body></html>

25
src/actions/loadData.js

@ -4,7 +4,7 @@ import { getServerAddress } from "../util/globals";
import { goTo404 } from "./navigation";
import { createStateFromQueryOrJSONs, createTreeTooState } from "./recomputeReduxState";
import { loadFrequencies } from "./frequencies";
import { fetchJSON } from "../util/serverInteraction";
import { fetchJSON, fetchWithErrorHandling } from "../util/serverInteraction";
import { warningNotification, errorNotification } from "./notifications";
import { hasExtension, getExtension } from "../util/extensions";
@ -21,18 +21,11 @@ import { hasExtension, getExtension } from "../util/extensions";
* @param {Object} additionalQueries: additional information to be parsed as a
* query string such as `type` (`String`) or `narrative` (`Boolean`).
*/
const getDatasetFromCharon = (prefix, {type, narrative=false}={}) => {
const getDatasetFromCharon = async (prefix, {type, narrative=false}={}) => {
let path = `${getServerAddress()}/${narrative?"getNarrative":"getDataset"}`;
path += `?prefix=${prefix}`;
if (type) path += `&type=${type}`;
const p = fetch(path)
.then((res) => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res;
});
return p;
return await fetchWithErrorHandling(path);
};
/**
@ -50,17 +43,9 @@ const getDatasetFromCharon = (prefix, {type, narrative=false}={}) => {
* @param {Object} additionalQueries: additional information to be parsed as a
* query string such as `type` (`String`) or `narrative` (`Boolean`).
*/
const getHardcodedData = (prefix, {type="mainJSON"}={}) => {
const getHardcodedData = async (prefix, {type="mainJSON"}={}) => {
const datapaths = getExtension("hardcodedDataPaths");
const p = fetch(datapaths[type])
.then((res) => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res;
});
return p;
return await fetchWithErrorHandling(datapaths[type]);
};
const getDataset = hasExtension("hardcodedDataPaths") ? getHardcodedData : getDatasetFromCharon;

22
src/util/serverInteraction.js

@ -1,11 +1,13 @@
export const fetchJSON = (path) => {
const p = fetch(path)
.then((res) => {
if (res.status !== 200) {
throw new Error(res.statusText);
}
return res;
})
.then((res) => res.json());
return p;
export const fetchWithErrorHandling = async (path) => {
const res = await fetch(path);
if (res.status !== 200) {
throw new Error(`${await res.text()} (${res.statusText})`);
}
return res;
};
export const fetchJSON = async (path) => {
const res = await fetchWithErrorHandling(path);
return await res.json();
};
Loading…
Cancel
Save