mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-18 02:39:44 +00:00
Improve REST HTTP response status codes
List all the HTTP status codes in the REST API tech doc. Only use 403 Forbidden for requests originating from a disallowed origin (ie, not localhost). - Return 400 for missing, unknown, duplicate and out-of-order form parts in POST requests. - Return 415 Unsupported Media Type for unsupported form part Content-Disposition and Content-Type (including unsupported charset). - Return 414 Request-URI Too Long for any buffer exhaustion while parsing request. - Return 419 Authentication Timeout for missing crypto secret.
This commit is contained in:
parent
e189bcf32a
commit
419364b5a9
@ -22,9 +22,10 @@ This document describes the second of these, the [HTTP REST][] API.
|
|||||||
|
|
||||||
### Protocol and port
|
### Protocol and port
|
||||||
|
|
||||||
The Serval DNA [HTTP REST][] API is an [HTTP 1.0][] server that accepts
|
The Serval DNA [HTTP REST][] API is an [HTTP 1.0][] server that only accepts
|
||||||
requests on the loopback interface (IPv4 address 127.0.0.1), TCP port 4110. It
|
requests on the loopback interface (IPv4 address 127.0.0.1), TCP port 4110. It
|
||||||
rejects requests that do not originate on the local host.
|
rejects requests that do not originate on the local host, by replying
|
||||||
|
[403](#forbidden).
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
@ -73,9 +74,9 @@ An HTTP REST request is a normal [HTTP 1.0][] [GET](#get) or [POST](#post):
|
|||||||
|
|
||||||
#### GET
|
#### GET
|
||||||
|
|
||||||
A **GET** request consists of an initial "GET" line, followed by zero or more
|
A **GET** request consists of an initial "GET" line containing the *path* and
|
||||||
header lines, followed by a blank line. As usual for HTTP, all lines are
|
*HTTP version*, followed by zero or more header lines, followed by a blank
|
||||||
terminated by an ASCII CR-LF sequence.
|
line. As usual for HTTP, all lines are terminated by an ASCII CR-LF sequence.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ unless otherwise documented.
|
|||||||
Some responses contain non-standard HTTP headers as part of the result they
|
Some responses contain non-standard HTTP headers as part of the result they
|
||||||
return to the client; for example, [Rhizome response headers](#rhizome-response-headers).
|
return to the client; for example, [Rhizome response headers](#rhizome-response-headers).
|
||||||
|
|
||||||
### Response status codes
|
### Response status code
|
||||||
|
|
||||||
The HTTP REST API response uses the [HTTP status code][] to indicate the
|
The HTTP REST API response uses the [HTTP status code][] to indicate the
|
||||||
outcome of the request as follows:
|
outcome of the request as follows:
|
||||||
@ -204,8 +205,12 @@ fetched entity forms the body of the response) if the request supplied a
|
|||||||
|
|
||||||
#### 400 Bad Request
|
#### 400 Bad Request
|
||||||
|
|
||||||
The HTTP request was malformed (incorrect syntax), and should not be repeated
|
The HTTP request was malformed, and should not be repeated without
|
||||||
without modifications.
|
modifications. This could be for several reasons:
|
||||||
|
- invalid syntax in the request header block
|
||||||
|
- a `POST` request MIME part is missing, duplicated or out of order
|
||||||
|
- a `POST` request was given an unsupported MIME part
|
||||||
|
- a `POST` request MIME part has missing or malformed content
|
||||||
|
|
||||||
#### 401 Unauthorized
|
#### 401 Unauthorized
|
||||||
|
|
||||||
@ -225,8 +230,8 @@ the missing credential:
|
|||||||
|
|
||||||
#### 403 Forbidden
|
#### 403 Forbidden
|
||||||
|
|
||||||
The request failed because the caller does not possess the necessary
|
The request failed because the server does not accept requests from the
|
||||||
cryptographic secret.
|
originating host.
|
||||||
|
|
||||||
#### 404 Not Found
|
#### 404 Not Found
|
||||||
|
|
||||||
@ -254,16 +259,35 @@ path that only supports [POST](#post), or vice versa.
|
|||||||
A `POST` request did not supply a [Content-Length](#request-content-length)
|
A `POST` request did not supply a [Content-Length](#request-content-length)
|
||||||
header.
|
header.
|
||||||
|
|
||||||
|
#### 414 Request-URI Too Long
|
||||||
|
|
||||||
|
The request failed because the [HTTP request URI][] was too long. The server
|
||||||
|
persists the path and a few other pieces of the request in a fixed size request
|
||||||
|
buffer, and this response is triggered if the collective size of these does not
|
||||||
|
leave enough buffer for receiving the remainder of the request.
|
||||||
|
|
||||||
#### 415 Unsupported Media Type
|
#### 415 Unsupported Media Type
|
||||||
|
|
||||||
The `POST` request [Content-Type](#request-content-type) header specified
|
A `POST` request failed because of an unsupported content type, which could be
|
||||||
an unsupported media type.
|
for several reasons:
|
||||||
|
- the request's [Content-Type](#request-content-type) header specified an
|
||||||
|
unsupported media type
|
||||||
|
- a MIME part Content-Disposition was not “form-data”
|
||||||
|
- a MIME part Content-Type was unsupported
|
||||||
|
- a MIME part Content-Type specified an unsupported charset
|
||||||
|
|
||||||
#### 416 Requested Range Not Satisfiable
|
#### 416 Requested Range Not Satisfiable
|
||||||
|
|
||||||
The [Range](#request-range) header specified a range whose start position falls
|
The [Range](#request-range) header specified a range whose start position falls
|
||||||
outside the size of the requested entity.
|
outside the size of the requested entity.
|
||||||
|
|
||||||
|
#### 419 Authentication Timeout
|
||||||
|
|
||||||
|
The request failed because the server does not possess and cannot derive the
|
||||||
|
necessary cryptographic secret or credential. For example, updating a Rhizome
|
||||||
|
bundle without providing the bundle secret. This code is not part of the HTTP
|
||||||
|
standard.
|
||||||
|
|
||||||
#### 422 Unprocessable Entity
|
#### 422 Unprocessable Entity
|
||||||
|
|
||||||
A `POST` request supplied data that was inconsistent or violates semantic
|
A `POST` request supplied data that was inconsistent or violates semantic
|
||||||
@ -288,14 +312,15 @@ development situations.
|
|||||||
The request cannot be performed because a necessary resource is temporarily
|
The request cannot be performed because a necessary resource is temporarily
|
||||||
unavailable due to a high volume of concurrent requests.
|
unavailable due to a high volume of concurrent requests.
|
||||||
|
|
||||||
This code was originally used by Rhizome operations if the server's manifest
|
The original use of this code was for Rhizome operations if the server's
|
||||||
table ran out of free manifests, which would only happen if there were many
|
manifest table ran out of free manifests, which would only happen if there were
|
||||||
concurrent Rhizome requests holding manifest structures open in server memory.
|
many concurrent Rhizome requests holding manifest structures open in server
|
||||||
It may be used for any resource that is occupied by running requests.
|
memory.
|
||||||
|
|
||||||
If [Serval DNA][] is ever limited to service only a few HTTP requests at a
|
This code may also be used to indicate temporary exhaustion of other finite
|
||||||
time, then this code will be returned to new requests that would exceed the
|
resources. For example, if [Serval DNA][] is ever limited to service only a
|
||||||
limit.
|
few HTTP requests at a time, then this code will be returned to new requests
|
||||||
|
that would exceed the limit.
|
||||||
|
|
||||||
#### 431 Request Header Fields Too Large
|
#### 431 Request Header Fields Too Large
|
||||||
|
|
||||||
@ -326,6 +351,32 @@ following cases:
|
|||||||
|
|
||||||
- a request [Range](#request-range) header specifies a multi range
|
- a request [Range](#request-range) header specifies a multi range
|
||||||
|
|
||||||
|
#### Cross-Origin Resource Sharing (CORS)
|
||||||
|
|
||||||
|
To support client-side JavaScript applications, Serval DNA has a limited
|
||||||
|
implementation of [Cross-Origin Resource Sharing][CORS]. If a request contains
|
||||||
|
an **Origin** header with either “null” or a single URI with scheme “http” or
|
||||||
|
“https” or “file”, hostname “localhost” or “127.0.0.1” (or empty in the case of
|
||||||
|
a “file” scheme), and optionally any port number, then the response will
|
||||||
|
contain three **Access-Control** headers granting permission for other pages on
|
||||||
|
the same site to access resources in the returned response.
|
||||||
|
|
||||||
|
For example, given the request:
|
||||||
|
|
||||||
|
GET /restful/keyring/identities.json HTTP/1.0
|
||||||
|
Origin: http://localhost:8080/
|
||||||
|
...
|
||||||
|
|
||||||
|
Serval DNA will respond:
|
||||||
|
|
||||||
|
HTTP/1.0 200 OK
|
||||||
|
Access-Control-Allow-Origin: http://localhost:8080
|
||||||
|
Access-Control-Allow-Methods: GET, POST, OPTIONS
|
||||||
|
Access-Control-Allow-Headers: Authorization
|
||||||
|
...
|
||||||
|
|
||||||
|
[CORS]: http://www.w3.org/TR/cors/
|
||||||
|
|
||||||
#### JSON result
|
#### JSON result
|
||||||
|
|
||||||
All responses that convey no special content return the following *JSON result*
|
All responses that convey no special content return the following *JSON result*
|
||||||
@ -343,8 +394,8 @@ line of the response.
|
|||||||
The `http_status_message` field is usually the same as the *reason phrase* text
|
The `http_status_message` field is usually the same as the *reason phrase* text
|
||||||
that follows the code in the first line of the HTTP response. This reason
|
that follows the code in the first line of the HTTP response. This reason
|
||||||
phrase may be a [standard phrase][status code], or it may be more explanatory;
|
phrase may be a [standard phrase][status code], or it may be more explanatory;
|
||||||
for example, *403 Forbidden* responses from Rhizome use the phrase, “Rhizome
|
for example, some *404* responses from Rhizome have phrases like, “Bundle not
|
||||||
operation failed”.
|
found”, “Payload not found”, etc.
|
||||||
|
|
||||||
Some responses augment the *JSON result* object with extra fields; for example,
|
Some responses augment the *JSON result* object with extra fields; for example,
|
||||||
[Rhizome JSON result](#rhizome-json-result).
|
[Rhizome JSON result](#rhizome-json-result).
|
||||||
@ -544,20 +595,21 @@ All Rhizome operations that involve fetching and/or inserting a single manifest
|
|||||||
into the Rhizome store return a *bundle status code*, which describes the
|
into the Rhizome store return a *bundle status code*, which describes the
|
||||||
outcome of the operation. Some codes have different meanings in the context of
|
outcome of the operation. Some codes have different meanings in the context of
|
||||||
a fetch or an insertion, and some codes can only be produced by insertions.
|
a fetch or an insertion, and some codes can only be produced by insertions.
|
||||||
|
The bundle status code determines the [HTTP response code](#response-status-code).
|
||||||
|
|
||||||
| code | meaning |
|
| code | HTTP | meaning |
|
||||||
|:----:|:------------------------------------------------------------------------------- |
|
|:----:|:----:|:------------------------------------------------------------------------------- |
|
||||||
| -1 | internal error |
|
| -1 | 500 | internal error |
|
||||||
| 0 | "new"; (fetch) bundle not found; (insert) bundle added to store |
|
| 0 | 201 | "new"; (fetch) bundle not found; (insert) bundle added to store |
|
||||||
| 1 | "same"; (fetch) bundle found; (insert) bundle already in store |
|
| 1 | 200 | "same"; (fetch) bundle found; (insert) bundle already in store |
|
||||||
| 2 | "duplicate"; (insert only) duplicate bundle already in store |
|
| 2 | 200 | "duplicate"; (insert only) duplicate bundle already in store |
|
||||||
| 3 | "old"; (insert only) newer version of bundle already in store |
|
| 3 | 202 | "old"; (insert only) newer version of bundle already in store |
|
||||||
| 4 | "invalid"; (insert only) manifest is invalid |
|
| 4 | 422 | "invalid"; (insert only) manifest is invalid |
|
||||||
| 5 | "fake"; (insert only) manifest signature is invalid |
|
| 5 | 419 | "fake"; (insert only) manifest signature is invalid |
|
||||||
| 6 | "inconsistent"; (insert only) manifest filesize/filehash does not match payload |
|
| 6 | 422 | "inconsistent"; (insert only) manifest filesize/filehash does not match payload |
|
||||||
| 7 | "no room"; (insert only) doesn't fit; store may contain more important bundles |
|
| 7 | 202 | "no room"; (insert only) doesn't fit; store may contain more important bundles |
|
||||||
| 8 | "readonly"; (insert only) cannot modify manifest because secret is unknown |
|
| 8 | 419 | "readonly"; (insert only) cannot modify manifest because secret is unknown |
|
||||||
| 9 | "busy"; Rhizome store database is currently busy (re-try) |
|
| 9 | 423 | "busy"; Rhizome store database is currently busy (re-try) |
|
||||||
|
|
||||||
#### Bundle status message
|
#### Bundle status message
|
||||||
|
|
||||||
@ -574,19 +626,21 @@ into the Rhizome store return a *payload status code*, which describes the
|
|||||||
outcome of the payload operation, and elaborates on the the reason for the
|
outcome of the payload operation, and elaborates on the the reason for the
|
||||||
accompanying *bundle status code*. Some codes have different meanings in the
|
accompanying *bundle status code*. Some codes have different meanings in the
|
||||||
context of a fetch or an insertion, and some codes can only be produced by
|
context of a fetch or an insertion, and some codes can only be produced by
|
||||||
insertions.
|
insertions. The payload status code overrides the [HTTP response
|
||||||
|
code](#response-status-code) derived from the [bundle status
|
||||||
|
code](#bundle-status-code) if it is numerically higher.
|
||||||
|
|
||||||
| code | meaning |
|
| code | HTTP | meaning |
|
||||||
|:----:|:--------------------------------------------------------------------- |
|
|:----:|:----:|:--------------------------------------------------------------------- |
|
||||||
| -1 | internal error |
|
| -1 | 500 | internal error |
|
||||||
| 0 | empty payload (zero length) |
|
| 0 | 201 | empty payload (zero length) |
|
||||||
| 1 | (fetch) payload not found; (insert) payload added to store |
|
| 1 | 201 | (fetch) payload not found; (insert) payload added to store |
|
||||||
| 2 | (fetch) payload found; (insert) payload already in store |
|
| 2 | 200 | (fetch) payload found; (insert) payload already in store |
|
||||||
| 3 | payload size does not match manifest *filesize* field |
|
| 3 | 422 | payload size does not match manifest *filesize* field |
|
||||||
| 4 | payload hash does not match manifest *filehash* field |
|
| 4 | 422 | payload hash does not match manifest *filehash* field |
|
||||||
| 5 | payload key unknown: (fetch) cannot decrypt; (insert) cannot encrypt |
|
| 5 | 419 | payload key unknown: (fetch) cannot decrypt; (insert) cannot encrypt |
|
||||||
| 6 | (insert only) payload is too big to fit in store |
|
| 6 | 202 | (insert only) payload is too big to fit in store |
|
||||||
| 7 | (insert only) payload evicted; other payloads are ranked higher |
|
| 7 | 202 | (insert only) payload evicted; other payloads are ranked higher |
|
||||||
|
|
||||||
#### Payload status message
|
#### Payload status message
|
||||||
|
|
||||||
@ -685,7 +739,7 @@ be *200 OK* and:
|
|||||||
nul (0) byte followed by the manifest's binary signature
|
nul (0) byte followed by the manifest's binary signature
|
||||||
|
|
||||||
If the **manifest is not found** in the local Rhizome store, then the response
|
If the **manifest is not found** in the local Rhizome store, then the response
|
||||||
will be *403 Forbidden* and:
|
will be *404 Bundle not found* and:
|
||||||
* the [bundle status code](#bundle-status-code) will be 0
|
* the [bundle status code](#bundle-status-code) will be 0
|
||||||
* the [payload status code](#payload-status-code), if present in the response,
|
* the [payload status code](#payload-status-code), if present in the response,
|
||||||
is not relevant, so must be ignored
|
is not relevant, so must be ignored
|
||||||
@ -715,17 +769,8 @@ then the response will be *200 OK* and:
|
|||||||
if the payload is encrypted (the manifest's `crypt` field is 1) then the
|
if the payload is encrypted (the manifest's `crypt` field is 1) then the
|
||||||
payload is not decrypted
|
payload is not decrypted
|
||||||
|
|
||||||
If the **manifest is found** in the local Rhizome store but the **payload is
|
|
||||||
not found**, then the response will be *403 Forbidden* and:
|
|
||||||
* the [bundle status code](#bundle-status-code) will be 1
|
|
||||||
* the [payload status code](#payload-status-code) will be 1
|
|
||||||
* the [Rhizome response bundle headers](#rhizome-response-bundle-headers) give
|
|
||||||
information about the found manifest
|
|
||||||
* the response's content is the [Rhizome JSON result](#rhizome-json-result)
|
|
||||||
object
|
|
||||||
|
|
||||||
If the **manifest is not found** in the local Rhizome store, then the response
|
If the **manifest is not found** in the local Rhizome store, then the response
|
||||||
will be *403 Forbidden* and:
|
will be *404 Bundle not found* and:
|
||||||
* the [bundle status code](#bundle-status-code) will be 0
|
* the [bundle status code](#bundle-status-code) will be 0
|
||||||
* the [payload status code](#payload-status-code), if present in the response,
|
* the [payload status code](#payload-status-code), if present in the response,
|
||||||
is not relevant, so must be ignored
|
is not relevant, so must be ignored
|
||||||
@ -734,6 +779,15 @@ will be *403 Forbidden* and:
|
|||||||
* the response's content is the [Rhizome JSON result](#rhizome-json-result)
|
* the response's content is the [Rhizome JSON result](#rhizome-json-result)
|
||||||
object
|
object
|
||||||
|
|
||||||
|
If the **manifest is found** in the local Rhizome store but the **payload is
|
||||||
|
not found**, then the response will be *404 Payload not found* and:
|
||||||
|
* the [bundle status code](#bundle-status-code) will be 1
|
||||||
|
* the [payload status code](#payload-status-code) will be 1
|
||||||
|
* the [Rhizome response bundle headers](#rhizome-response-bundle-headers) give
|
||||||
|
information about the found manifest
|
||||||
|
* the response's content is the [Rhizome JSON result](#rhizome-json-result)
|
||||||
|
object
|
||||||
|
|
||||||
### GET /restful/rhizome/BID/decrypted.bin
|
### GET /restful/rhizome/BID/decrypted.bin
|
||||||
|
|
||||||
Fetches the decrypted payload for the bundle whose id is `BID` (64 hex digits),
|
Fetches the decrypted payload for the bundle whose id is `BID` (64 hex digits),
|
||||||
|
@ -237,7 +237,7 @@ static int _reserve(struct http_request *r, const char **resp, const char *src,
|
|||||||
assert(r->reserved <= reslim);
|
assert(r->reserved <= reslim);
|
||||||
size_t siz = sizeof(char**) + len + 1;
|
size_t siz = sizeof(char**) + len + 1;
|
||||||
if (r->reserved + siz > reslim) {
|
if (r->reserved + siz > reslim) {
|
||||||
r->response.result_code = 414;
|
r->response.result_code = 414; // Request-URI Too Long
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (r->reserved + siz > r->parsed) {
|
if (r->reserved + siz > r->parsed) {
|
||||||
@ -1972,6 +1972,7 @@ static const char *http_reason_phrase(int response_code)
|
|||||||
case 414: return "Request-URI Too Long";
|
case 414: return "Request-URI Too Long";
|
||||||
case 415: return "Unsupported Media Type";
|
case 415: return "Unsupported Media Type";
|
||||||
case 416: return "Requested Range Not Satisfiable";
|
case 416: return "Requested Range Not Satisfiable";
|
||||||
|
case 419: return "Authentication Timeout";
|
||||||
case 422: return "Unprocessable Entity";
|
case 422: return "Unprocessable Entity";
|
||||||
case 423: return "Locked";
|
case 423: return "Locked";
|
||||||
case 429: return "Too Many Requests";
|
case 429: return "Too Many Requests";
|
||||||
|
46
httpd.c
46
httpd.c
@ -357,9 +357,12 @@ int authorize_restful(struct http_request *r)
|
|||||||
{
|
{
|
||||||
if (!is_from_loopback(r))
|
if (!is_from_loopback(r))
|
||||||
return 403;
|
return 403;
|
||||||
if (r->request_header.origin.hostname) {
|
// If a CORS Origin: header was supplied, then if it specifies a local site, then respond with
|
||||||
assert(r->request_header.origin.scheme);
|
// Access-Control-Allow-Origin and Access-Control-Allow-Methods headers that permit other pages in
|
||||||
if ( ( ( strcmp(r->request_header.origin.scheme, "http") == 0
|
// the same local site to request this page, otherwise respond with 403 Forbidden.
|
||||||
|
if (r->request_header.origin.null || r->request_header.origin.scheme[0]) {
|
||||||
|
if ( r->request_header.origin.null
|
||||||
|
|| ( ( strcmp(r->request_header.origin.scheme, "http") == 0
|
||||||
|| strcmp(r->request_header.origin.scheme, "https") == 0
|
|| strcmp(r->request_header.origin.scheme, "https") == 0
|
||||||
)
|
)
|
||||||
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
&& ( strcmp(r->request_header.origin.hostname, "localhost") == 0
|
||||||
@ -373,20 +376,9 @@ int authorize_restful(struct http_request *r)
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
strbuf sb = strbuf_local(r->response.header.allow_origin, sizeof r->response.header.allow_origin);
|
r->response.header.allow_origin = r->request_header.origin;
|
||||||
strbuf_puts(sb, r->request_header.origin.scheme);
|
|
||||||
strbuf_puts(sb, "://");
|
|
||||||
strbuf_puts(sb, r->request_header.origin.hostname);
|
|
||||||
if (r->request_header.origin.port) {
|
|
||||||
strbuf_sprintf(sb, ":%u", r->request_header.origin.port);
|
|
||||||
}
|
|
||||||
if (!strbuf_overrun(sb)) {
|
|
||||||
r->response.header.allow_methods = "GET, POST, OPTIONS";
|
r->response.header.allow_methods = "GET, POST, OPTIONS";
|
||||||
r->response.header.allow_headers = "Authorization";
|
r->response.header.allow_headers = "Authorization";
|
||||||
} else {
|
|
||||||
r->response.header.allow_origin[0] = '\0';
|
|
||||||
return 403;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return 403;
|
return 403;
|
||||||
}
|
}
|
||||||
@ -413,7 +405,7 @@ int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_
|
|||||||
);
|
);
|
||||||
strbuf msg = strbuf_alloca(100);
|
strbuf msg = strbuf_alloca(100);
|
||||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
http_request_simple_response(&r->http, 400, strbuf_str(msg));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
memcpy(textbuf + *textlenp, buf, len);
|
memcpy(textbuf + *textlenp, buf, len);
|
||||||
@ -442,8 +434,8 @@ int form_buf_malloc_accumulate(httpd_request *r, const char *partname, struct fo
|
|||||||
);
|
);
|
||||||
strbuf msg = strbuf_alloca(100);
|
strbuf msg = strbuf_alloca(100);
|
||||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
http_request_simple_response(&r->http, 400, strbuf_str(msg));
|
||||||
return 403;
|
return 400;
|
||||||
}
|
}
|
||||||
if (newlen > f->buffer_alloc_size) {
|
if (newlen > f->buffer_alloc_size) {
|
||||||
if ((f->buffer = erealloc(f->buffer, newlen)) == NULL) {
|
if ((f->buffer = erealloc(f->buffer, newlen)) == NULL) {
|
||||||
@ -468,7 +460,7 @@ void form_buf_malloc_release(struct form_buf_malloc *f)
|
|||||||
f->size_limit = 0;
|
f->size_limit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_response_content_type(httpd_request *r, const char *what, const struct mime_content_type *ct)
|
int http_response_content_type(httpd_request *r, uint16_t result, const char *what, const struct mime_content_type *ct)
|
||||||
{
|
{
|
||||||
DEBUGF(httpd, "%s Content-Type: %s/%s%s%s%s%s", what, ct->type, ct->subtype,
|
DEBUGF(httpd, "%s Content-Type: %s/%s%s%s%s%s", what, ct->type, ct->subtype,
|
||||||
ct->charset[0] ? "; charset=" : "",
|
ct->charset[0] ? "; charset=" : "",
|
||||||
@ -486,26 +478,26 @@ int http_response_content_type(httpd_request *r, const char *what, const struct
|
|||||||
strbuf_sprintf(msg, "; charset=%s", ct->charset);
|
strbuf_sprintf(msg, "; charset=%s", ct->charset);
|
||||||
if (ct->multipart_boundary[0])
|
if (ct->multipart_boundary[0])
|
||||||
strbuf_sprintf(msg, "; boundary=%s", ct->multipart_boundary);
|
strbuf_sprintf(msg, "; boundary=%s", ct->multipart_boundary);
|
||||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
http_request_simple_response(&r->http, result, strbuf_str(msg));
|
||||||
return 403;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_response_content_disposition(httpd_request *r, const char *what, const char *type)
|
int http_response_content_disposition(httpd_request *r, uint16_t result, const char *what, const char *type)
|
||||||
{
|
{
|
||||||
DEBUGF(httpd, "%s Content-Disposition: %s %s", what, type);
|
DEBUGF(httpd, "%s Content-Disposition: %s %s", what, type);
|
||||||
strbuf msg = strbuf_alloca(100);
|
strbuf msg = strbuf_alloca(100);
|
||||||
strbuf_sprintf(msg, "%s Content-Disposition: %s", what, type);
|
strbuf_sprintf(msg, "%s Content-Disposition: %s", what, type);
|
||||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
http_request_simple_response(&r->http, result, strbuf_str(msg)); // Unsupported Media Type
|
||||||
return 403;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen)
|
int http_response_form_part(httpd_request *r, uint16_t result, const char *what, const char *partname, const char *text, size_t textlen)
|
||||||
{
|
{
|
||||||
DEBUGF(httpd, "%s \"%s\" form part %s", what, partname, text ? alloca_toprint(-1, text, textlen) : "");
|
DEBUGF(httpd, "%s \"%s\" form part %s", what, partname, text ? alloca_toprint(-1, text, textlen) : "");
|
||||||
strbuf msg = strbuf_alloca(100);
|
strbuf msg = strbuf_alloca(100);
|
||||||
strbuf_sprintf(msg, "%s \"%s\" form part", what, partname);
|
strbuf_sprintf(msg, "%s \"%s\" form part", what, partname);
|
||||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
http_request_simple_response(&r->http, result, strbuf_str(msg)); // Unsupported Media Type
|
||||||
return 403;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_response_init_content_range(httpd_request *r, size_t resource_length)
|
int http_response_init_content_range(httpd_request *r, size_t resource_length)
|
||||||
|
6
httpd.h
6
httpd.h
@ -235,9 +235,9 @@ DECLARE_SECTION(struct http_handler, httpd);
|
|||||||
|
|
||||||
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
|
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
|
||||||
int authorize_restful(struct http_request *r);
|
int authorize_restful(struct http_request *r);
|
||||||
int http_response_content_type(httpd_request *r, const char *what, const struct mime_content_type *ct);
|
int http_response_content_type(httpd_request *r, uint16_t result, const char *what, const struct mime_content_type *ct);
|
||||||
int http_response_content_disposition(httpd_request *r, const char *what, const char *type);
|
int http_response_content_disposition(httpd_request *r, uint16_t result, const char *what, const char *type);
|
||||||
int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen);
|
int http_response_form_part(httpd_request *r, uint16_t result, const char *what, const char *partname, const char *text, size_t textlen);
|
||||||
int http_response_init_content_range(httpd_request *r, size_t resource_length);
|
int http_response_init_content_range(httpd_request *r, size_t resource_length);
|
||||||
int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len);
|
int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len);
|
||||||
|
|
||||||
|
@ -46,7 +46,9 @@ public class MeshMSCommon
|
|||||||
{
|
{
|
||||||
if (!"application/json".equals(conn.getContentType()))
|
if (!"application/json".equals(conn.getContentType()))
|
||||||
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
|
switch (conn.getResponseCode()) {
|
||||||
|
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||||
|
case 419: // Authentication Timeout, for missing secret
|
||||||
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "UTF-8"));
|
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "UTF-8"));
|
||||||
Status status = decodeRestfulStatus(json);
|
Status status = decodeRestfulStatus(json);
|
||||||
throwRestfulResponseExceptions(status, conn.getURL());
|
throwRestfulResponseExceptions(status, conn.getURL());
|
||||||
|
@ -96,6 +96,7 @@ public class RhizomeCommon
|
|||||||
switch (status.http_status_code) {
|
switch (status.http_status_code) {
|
||||||
case HttpURLConnection.HTTP_FORBIDDEN: // for crypto failure (missing secret)
|
case HttpURLConnection.HTTP_FORBIDDEN: // for crypto failure (missing secret)
|
||||||
case HttpURLConnection.HTTP_NOT_FOUND: // for unknown BID
|
case HttpURLConnection.HTTP_NOT_FOUND: // for unknown BID
|
||||||
|
case 419: // Authentication Timeout, for missing secret
|
||||||
case 422: // Unprocessable Entity, for invalid/malformed manifest
|
case 422: // Unprocessable Entity, for invalid/malformed manifest
|
||||||
case 423: // Locked, for database busy
|
case 423: // Locked, for database busy
|
||||||
case 429: // Too Many Requests, for out of manifests
|
case 429: // Too Many Requests, for out of manifests
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Serval DNA HTTP RESTful interface
|
Serval DNA HTTP RESTful interface
|
||||||
Copyright (C) 2013,2014 Serval Project Inc.
|
Copyright (C) 2013-2015 Serval Project Inc.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License
|
modify it under the terms of the GNU General Public License
|
||||||
@ -86,7 +86,7 @@ static int http_request_keyring_response_identity(struct httpd_request *r, uint1
|
|||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
keyring_identity_extract(id, &sidp, &did, &name);
|
keyring_identity_extract(id, &sidp, &did, &name);
|
||||||
if (!sidp)
|
if (!sidp)
|
||||||
return http_request_keyring_response(r, 501, "Identity has no SID");
|
return http_request_keyring_response(r, 500, "Identity has no SID");
|
||||||
struct json_atom json_id;
|
struct json_atom json_id;
|
||||||
struct json_key_value json_id_kv[3];
|
struct json_key_value json_id_kv[3];
|
||||||
struct json_atom json_sid;
|
struct json_atom json_sid;
|
||||||
@ -213,9 +213,9 @@ static int restful_keyring_add(httpd_request *r, const char *remainder)
|
|||||||
const char *pin = http_request_get_query_param(&r->http, "pin");
|
const char *pin = http_request_get_query_param(&r->http, "pin");
|
||||||
const keyring_identity *id = keyring_create_identity(keyring, pin ? pin : "");
|
const keyring_identity *id = keyring_create_identity(keyring, pin ? pin : "");
|
||||||
if (id == NULL)
|
if (id == NULL)
|
||||||
return http_request_keyring_response(r, 501, "Could not create identity");
|
return http_request_keyring_response(r, 500, "Could not create identity");
|
||||||
if (keyring_commit(keyring) == -1)
|
if (keyring_commit(keyring) == -1)
|
||||||
return http_request_keyring_response(r, 501, "Could not store new identity");
|
return http_request_keyring_response(r, 500, "Could not store new identity");
|
||||||
return http_request_keyring_response_identity(r, 200, CONTENT_TYPE_JSON, id);
|
return http_request_keyring_response_identity(r, 200, CONTENT_TYPE_JSON, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,10 +231,10 @@ static int restful_keyring_set(httpd_request *r, const char *remainder)
|
|||||||
keyring_iterator it;
|
keyring_iterator it;
|
||||||
keyring_iterator_start(keyring, &it);
|
keyring_iterator_start(keyring, &it);
|
||||||
if (!keyring_find_sid(&it, &r->sid1))
|
if (!keyring_find_sid(&it, &r->sid1))
|
||||||
return http_request_keyring_response(r, 404, NULL);
|
return http_request_keyring_response(r, 404, "Identity not found");
|
||||||
if (keyring_set_did(it.identity, did ? did : "", name ? name : "") == -1)
|
if (keyring_set_did(it.identity, did ? did : "", name ? name : "") == -1)
|
||||||
return http_request_keyring_response(r, 501, "Could not set identity DID/Name");
|
return http_request_keyring_response(r, 500, "Could not set identity DID/Name");
|
||||||
if (keyring_commit(keyring) == -1)
|
if (keyring_commit(keyring) == -1)
|
||||||
return http_request_keyring_response(r, 501, "Could not store new identity");
|
return http_request_keyring_response(r, 500, "Could not store new identity");
|
||||||
return http_request_keyring_response_identity(r, 200, CONTENT_TYPE_JSON, it.identity);
|
return http_request_keyring_response_identity(r, 200, CONTENT_TYPE_JSON, it.identity);
|
||||||
}
|
}
|
||||||
|
@ -71,16 +71,22 @@ static int strn_to_meshms_token(const char *str, rhizome_bid_t *bidp, uint64_t *
|
|||||||
static int http_request_meshms_response(struct httpd_request *r, uint16_t result, const char *message, enum meshms_status status)
|
static int http_request_meshms_response(struct httpd_request *r, uint16_t result, const char *message, enum meshms_status status)
|
||||||
{
|
{
|
||||||
uint16_t meshms_result = 0;
|
uint16_t meshms_result = 0;
|
||||||
|
const char *meshms_message = NULL;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case MESHMS_STATUS_OK:
|
case MESHMS_STATUS_OK:
|
||||||
meshms_result = 200;
|
meshms_result = 200;
|
||||||
break;
|
break;
|
||||||
case MESHMS_STATUS_UPDATED:
|
case MESHMS_STATUS_UPDATED:
|
||||||
meshms_result = 201;
|
meshms_result = 201; // Created
|
||||||
|
meshms_message = "Updated";
|
||||||
break;
|
break;
|
||||||
case MESHMS_STATUS_SID_LOCKED:
|
case MESHMS_STATUS_SID_LOCKED:
|
||||||
|
meshms_result = 419; // Authentication Timeout
|
||||||
|
meshms_message = "Identity locked";
|
||||||
|
break;
|
||||||
case MESHMS_STATUS_PROTOCOL_FAULT:
|
case MESHMS_STATUS_PROTOCOL_FAULT:
|
||||||
meshms_result = 403;
|
meshms_result = 500;
|
||||||
|
meshms_message = "MeshMS protocol fault";
|
||||||
break;
|
break;
|
||||||
case MESHMS_STATUS_ERROR:
|
case MESHMS_STATUS_ERROR:
|
||||||
meshms_result = 500;
|
meshms_result = 500;
|
||||||
@ -101,10 +107,10 @@ static int http_request_meshms_response(struct httpd_request *r, uint16_t result
|
|||||||
}
|
}
|
||||||
if (meshms_result > result) {
|
if (meshms_result > result) {
|
||||||
result = meshms_result;
|
result = meshms_result;
|
||||||
message = NULL;
|
message = meshms_message;
|
||||||
}
|
}
|
||||||
assert(result != 0);
|
assert(result != 0);
|
||||||
http_request_simple_response(&r->http, result, message ? message : result == 403 ? "MeshMS operation failed" : NULL);
|
http_request_simple_response(&r->http, result, message);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +126,7 @@ static int restful_meshms_(httpd_request *r, const char *remainder)
|
|||||||
{
|
{
|
||||||
r->http.response.header.content_type = CONTENT_TYPE_JSON;
|
r->http.response.header.content_type = CONTENT_TYPE_JSON;
|
||||||
if (!is_rhizome_http_enabled())
|
if (!is_rhizome_http_enabled())
|
||||||
return 403;
|
return 404;
|
||||||
int ret = authorize_restful(&r->http);
|
int ret = authorize_restful(&r->http);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -591,7 +597,7 @@ static int send_mime_part_end(struct http_request *hr)
|
|||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
if (r->u.sendmsg.current_part == PART_MESSAGE) {
|
if (r->u.sendmsg.current_part == PART_MESSAGE) {
|
||||||
if (r->u.sendmsg.message.length == 0)
|
if (r->u.sendmsg.message.length == 0)
|
||||||
return http_response_form_part(r, "Invalid (empty)", PART_MESSAGE, NULL, 0);
|
return http_response_form_part(r, 400, "Invalid (empty)", PART_MESSAGE, NULL, 0);
|
||||||
r->u.sendmsg.received_message = 1;
|
r->u.sendmsg.received_message = 1;
|
||||||
DEBUGF(httpd, "received %s = %s", PART_MESSAGE, alloca_toprint(-1, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length));
|
DEBUGF(httpd, "received %s = %s", PART_MESSAGE, alloca_toprint(-1, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length));
|
||||||
} else
|
} else
|
||||||
@ -604,23 +610,23 @@ static int send_mime_part_header(struct http_request *hr, const struct mime_part
|
|||||||
{
|
{
|
||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
if (strcmp(h->content_disposition.type, "form-data") != 0)
|
if (strcmp(h->content_disposition.type, "form-data") != 0)
|
||||||
return http_response_content_disposition(r, "Unsupported", h->content_disposition.type);
|
return http_response_content_disposition(r, 415, "Unsupported", h->content_disposition.type);
|
||||||
if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) {
|
if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) {
|
||||||
if (r->u.sendmsg.received_message)
|
if (r->u.sendmsg.received_message)
|
||||||
return http_response_form_part(r, "Duplicate", PART_MESSAGE, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_MESSAGE, NULL, 0);
|
||||||
r->u.sendmsg.current_part = PART_MESSAGE;
|
r->u.sendmsg.current_part = PART_MESSAGE;
|
||||||
form_buf_malloc_init(&r->u.sendmsg.message, MESHMS_MESSAGE_MAX_LEN);
|
form_buf_malloc_init(&r->u.sendmsg.message, MESHMS_MESSAGE_MAX_LEN);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return http_response_form_part(r, "Unsupported", h->content_disposition.name, NULL, 0);
|
return http_response_form_part(r, 415, "Unsupported", h->content_disposition.name, NULL, 0);
|
||||||
if (!h->content_type.type[0] || !h->content_type.subtype[0])
|
if (!h->content_type.type[0] || !h->content_type.subtype[0])
|
||||||
return http_response_content_type(r, "Missing", &h->content_type);
|
return http_response_content_type(r, 400, "Missing", &h->content_type);
|
||||||
if (strcmp(h->content_type.type, "text") != 0 || strcmp(h->content_type.subtype, "plain") != 0)
|
if (strcmp(h->content_type.type, "text") != 0 || strcmp(h->content_type.subtype, "plain") != 0)
|
||||||
return http_response_content_type(r, "Unsupported", &h->content_type);
|
return http_response_content_type(r, 415, "Unsupported", &h->content_type);
|
||||||
if (!h->content_type.charset[0])
|
if (!h->content_type.charset[0])
|
||||||
return http_response_content_type(r, "Missing charset", &h->content_type);
|
return http_response_content_type(r, 400, "Missing charset", &h->content_type);
|
||||||
if (strcmp(h->content_type.charset, "utf-8") != 0)
|
if (strcmp(h->content_type.charset, "utf-8") != 0)
|
||||||
return http_response_content_type(r, "Unsupported charset", &h->content_type);
|
return http_response_content_type(r, 415, "Unsupported charset", &h->content_type);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,7 +644,7 @@ static int restful_meshms_sendmessage_end(struct http_request *hr)
|
|||||||
{
|
{
|
||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
if (!r->u.sendmsg.received_message)
|
if (!r->u.sendmsg.received_message)
|
||||||
return http_response_form_part(r, "Missing", PART_MESSAGE, NULL, 0);
|
return http_response_form_part(r, 400, "Missing", PART_MESSAGE, NULL, 0);
|
||||||
assert(r->u.sendmsg.message.length > 0);
|
assert(r->u.sendmsg.message.length > 0);
|
||||||
assert(r->u.sendmsg.message.length <= MESHMS_MESSAGE_MAX_LEN);
|
assert(r->u.sendmsg.message.length <= MESHMS_MESSAGE_MAX_LEN);
|
||||||
enum meshms_status status;
|
enum meshms_status status;
|
||||||
|
@ -74,20 +74,20 @@ static int http_request_rhizome_response(struct httpd_request *r, uint16_t resul
|
|||||||
{
|
{
|
||||||
uint16_t rhizome_result = 0;
|
uint16_t rhizome_result = 0;
|
||||||
switch (r->bundle_status) {
|
switch (r->bundle_status) {
|
||||||
case RHIZOME_BUNDLE_STATUS_NEW:
|
|
||||||
rhizome_result = 201; // Created
|
|
||||||
break;
|
|
||||||
case RHIZOME_BUNDLE_STATUS_SAME:
|
case RHIZOME_BUNDLE_STATUS_SAME:
|
||||||
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
|
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
|
||||||
rhizome_result = 200; // OK
|
rhizome_result = 200; // OK
|
||||||
break;
|
break;
|
||||||
|
case RHIZOME_BUNDLE_STATUS_NEW:
|
||||||
|
rhizome_result = 201; // Created
|
||||||
|
break;
|
||||||
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
|
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
|
||||||
case RHIZOME_BUNDLE_STATUS_OLD:
|
case RHIZOME_BUNDLE_STATUS_OLD:
|
||||||
rhizome_result = 202; // Accepted
|
rhizome_result = 202; // Accepted
|
||||||
break;
|
break;
|
||||||
case RHIZOME_BUNDLE_STATUS_FAKE:
|
case RHIZOME_BUNDLE_STATUS_FAKE:
|
||||||
case RHIZOME_BUNDLE_STATUS_READONLY:
|
case RHIZOME_BUNDLE_STATUS_READONLY:
|
||||||
rhizome_result = 403; // Forbidden
|
rhizome_result = 419; // Authentication Timeout
|
||||||
break;
|
break;
|
||||||
case RHIZOME_BUNDLE_STATUS_INVALID:
|
case RHIZOME_BUNDLE_STATUS_INVALID:
|
||||||
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
|
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
|
||||||
@ -117,9 +117,11 @@ static int http_request_rhizome_response(struct httpd_request *r, uint16_t resul
|
|||||||
}
|
}
|
||||||
rhizome_result = 0;
|
rhizome_result = 0;
|
||||||
switch (r->payload_status) {
|
switch (r->payload_status) {
|
||||||
case RHIZOME_PAYLOAD_STATUS_NEW:
|
|
||||||
case RHIZOME_PAYLOAD_STATUS_STORED:
|
case RHIZOME_PAYLOAD_STATUS_STORED:
|
||||||
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
||||||
|
rhizome_result = 200;
|
||||||
|
break;
|
||||||
|
case RHIZOME_PAYLOAD_STATUS_NEW:
|
||||||
rhizome_result = 201;
|
rhizome_result = 201;
|
||||||
break;
|
break;
|
||||||
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
|
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
|
||||||
@ -127,7 +129,7 @@ static int http_request_rhizome_response(struct httpd_request *r, uint16_t resul
|
|||||||
rhizome_result = 202; // Accepted
|
rhizome_result = 202; // Accepted
|
||||||
break;
|
break;
|
||||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||||
rhizome_result = 403; // Forbidden
|
rhizome_result = 419; // Authentication Timeout
|
||||||
break;
|
break;
|
||||||
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
||||||
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
||||||
@ -395,7 +397,7 @@ static int insert_mime_part_start(struct http_request *hr)
|
|||||||
static int insert_make_manifest(httpd_request *r)
|
static int insert_make_manifest(httpd_request *r)
|
||||||
{
|
{
|
||||||
if (!r->u.insert.received_manifest)
|
if (!r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0);
|
||||||
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
||||||
return http_request_rhizome_response(r, 429, "Manifest table full", NULL); // Too Many Requests
|
return http_request_rhizome_response(r, 429, "Manifest table full", NULL); // Too Many Requests
|
||||||
assert(r->u.insert.manifest.length <= sizeof r->manifest->manifestdata);
|
assert(r->u.insert.manifest.length <= sizeof r->manifest->manifestdata);
|
||||||
@ -425,7 +427,7 @@ static int insert_make_manifest(httpd_request *r)
|
|||||||
r->u.insert.received_bundleid ? &r->bid: NULL,
|
r->u.insert.received_bundleid ? &r->bid: NULL,
|
||||||
r->u.insert.received_secret ? &r->u.insert.bundle_secret : NULL,
|
r->u.insert.received_secret ? &r->u.insert.bundle_secret : NULL,
|
||||||
r->u.insert.received_author ? &r->u.insert.author: NULL,
|
r->u.insert.received_author ? &r->u.insert.author: NULL,
|
||||||
NULL, 0, NULL, strbuf_local(message, sizeof message));
|
NULL, 0, NULL, strbuf_local_buf(message));
|
||||||
int result_valid = 0;
|
int result_valid = 0;
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case RHIZOME_ADD_FILE_ERROR:
|
case RHIZOME_ADD_FILE_ERROR:
|
||||||
@ -447,7 +449,7 @@ static int insert_make_manifest(httpd_request *r)
|
|||||||
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity
|
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity
|
||||||
case RHIZOME_ADD_FILE_WRONG_SECRET:
|
case RHIZOME_ADD_FILE_WRONG_SECRET:
|
||||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY; // TODO separate enum for CLI return codes
|
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY; // TODO separate enum for CLI return codes
|
||||||
return http_request_rhizome_response(r, 403, message, NULL); // Forbidden
|
return http_request_rhizome_response(r, 419, message, NULL); // Authentication Timeout
|
||||||
}
|
}
|
||||||
if (!result_valid)
|
if (!result_valid)
|
||||||
FATALF("result = %d", result);
|
FATALF("result = %d", result);
|
||||||
@ -464,55 +466,55 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
|
|||||||
{
|
{
|
||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
if (strcmp(h->content_disposition.type, "form-data") != 0)
|
if (strcmp(h->content_disposition.type, "form-data") != 0)
|
||||||
return http_response_content_disposition(r, "Unsupported", h->content_disposition.type);
|
return http_response_content_disposition(r, 415, "Unsupported", h->content_disposition.type);
|
||||||
if (strcmp(h->content_disposition.name, PART_AUTHOR) == 0) {
|
if (strcmp(h->content_disposition.name, PART_AUTHOR) == 0) {
|
||||||
if (r->u.insert.received_author)
|
if (r->u.insert.received_author)
|
||||||
return http_response_form_part(r, "Duplicate", PART_AUTHOR, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_AUTHOR, NULL, 0);
|
||||||
// Reject a request if this parameter comes after the manifest part.
|
// Reject a request if this parameter comes after the manifest part.
|
||||||
if (r->u.insert.received_manifest)
|
if (r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Spurious", PART_AUTHOR, NULL, 0);
|
return http_response_form_part(r, 400, "Spurious", PART_AUTHOR, NULL, 0);
|
||||||
r->u.insert.current_part = PART_AUTHOR;
|
r->u.insert.current_part = PART_AUTHOR;
|
||||||
assert(r->u.insert.author_hex_len == 0);
|
assert(r->u.insert.author_hex_len == 0);
|
||||||
}
|
}
|
||||||
else if (strcmp(h->content_disposition.name, PART_SECRET) == 0) {
|
else if (strcmp(h->content_disposition.name, PART_SECRET) == 0) {
|
||||||
if (r->u.insert.received_secret)
|
if (r->u.insert.received_secret)
|
||||||
return http_response_form_part(r, "Duplicate", PART_SECRET, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_SECRET, NULL, 0);
|
||||||
// Reject a request if this parameter comes after the manifest part.
|
// Reject a request if this parameter comes after the manifest part.
|
||||||
if (r->u.insert.received_manifest)
|
if (r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Spurious", PART_SECRET, NULL, 0);
|
return http_response_form_part(r, 400, "Spurious", PART_SECRET, NULL, 0);
|
||||||
r->u.insert.current_part = PART_SECRET;
|
r->u.insert.current_part = PART_SECRET;
|
||||||
assert(r->u.insert.secret_text_len == 0);
|
assert(r->u.insert.secret_text_len == 0);
|
||||||
}
|
}
|
||||||
else if (strcmp(h->content_disposition.name, PART_BUNDLEID) == 0) {
|
else if (strcmp(h->content_disposition.name, PART_BUNDLEID) == 0) {
|
||||||
if (r->u.insert.received_bundleid)
|
if (r->u.insert.received_bundleid)
|
||||||
return http_response_form_part(r, "Duplicate", PART_BUNDLEID, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_BUNDLEID, NULL, 0);
|
||||||
// Reject a request if this parameter comes after the manifest part.
|
// Reject a request if this parameter comes after the manifest part.
|
||||||
if (r->u.insert.received_manifest)
|
if (r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Spurious", PART_BUNDLEID, NULL, 0);
|
return http_response_form_part(r, 400, "Spurious", PART_BUNDLEID, NULL, 0);
|
||||||
r->u.insert.current_part = PART_BUNDLEID;
|
r->u.insert.current_part = PART_BUNDLEID;
|
||||||
assert(r->u.insert.bid_text_len == 0);
|
assert(r->u.insert.bid_text_len == 0);
|
||||||
}
|
}
|
||||||
else if (strcmp(h->content_disposition.name, PART_MANIFEST) == 0) {
|
else if (strcmp(h->content_disposition.name, PART_MANIFEST) == 0) {
|
||||||
// Reject a request if it has a repeated manifest part.
|
// Reject a request if it has a repeated manifest part.
|
||||||
if (r->u.insert.received_manifest)
|
if (r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Duplicate", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_MANIFEST, NULL, 0);
|
||||||
form_buf_malloc_init(&r->u.insert.manifest, MAX_MANIFEST_BYTES);
|
form_buf_malloc_init(&r->u.insert.manifest, MAX_MANIFEST_BYTES);
|
||||||
if ( strcmp(h->content_type.type, "rhizome") != 0
|
if ( strcmp(h->content_type.type, "rhizome") != 0
|
||||||
|| strcmp(h->content_type.subtype, "manifest") != 0
|
|| strcmp(h->content_type.subtype, "manifest") != 0
|
||||||
)
|
)
|
||||||
return http_response_form_part(r, "Unsupported Content-Type in", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 415, "Unsupported Content-Type in", PART_MANIFEST, NULL, 0);
|
||||||
if ((strcmp(h->content_type.format, "text+binarysig") != 0)
|
if ((strcmp(h->content_type.format, "text+binarysig") != 0)
|
||||||
&&strlen(h->content_type.format))
|
&&strlen(h->content_type.format))
|
||||||
return http_response_form_part(r, "Unsupported rhizome/manifest format in", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 415, "Unsupported rhizome/manifest format in", PART_MANIFEST, NULL, 0);
|
||||||
r->u.insert.current_part = PART_MANIFEST;
|
r->u.insert.current_part = PART_MANIFEST;
|
||||||
}
|
}
|
||||||
else if (strcmp(h->content_disposition.name, PART_PAYLOAD) == 0) {
|
else if (strcmp(h->content_disposition.name, PART_PAYLOAD) == 0) {
|
||||||
// Reject a request if it has a repeated payload part.
|
// Reject a request if it has a repeated payload part.
|
||||||
if (r->u.insert.received_payload)
|
if (r->u.insert.received_payload)
|
||||||
return http_response_form_part(r, "Duplicate", PART_PAYLOAD, NULL, 0);
|
return http_response_form_part(r, 400, "Duplicate", PART_PAYLOAD, NULL, 0);
|
||||||
// Reject a request if it has a missing manifest part preceding the payload part.
|
// Reject a request if it has a missing manifest part preceding the payload part.
|
||||||
if (!r->u.insert.received_manifest)
|
if (!r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0);
|
||||||
assert(r->manifest != NULL);
|
assert(r->manifest != NULL);
|
||||||
r->u.insert.current_part = PART_PAYLOAD;
|
r->u.insert.current_part = PART_PAYLOAD;
|
||||||
// If the manifest does not contain a 'name' field, then assign it from the payload filename.
|
// If the manifest does not contain a 'name' field, then assign it from the payload filename.
|
||||||
@ -547,7 +549,7 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
|
|||||||
r->u.insert.payload_size = 0;
|
r->u.insert.payload_size = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return http_response_form_part(r, "Unsupported", h->content_disposition.name, NULL, 0);
|
return http_response_form_part(r, 400, "Unsupported", h->content_disposition.name, NULL, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,19 +605,19 @@ static int insert_mime_part_end(struct http_request *hr)
|
|||||||
if ( r->u.insert.author_hex_len != sizeof r->u.insert.author_hex
|
if ( r->u.insert.author_hex_len != sizeof r->u.insert.author_hex
|
||||||
|| strn_to_sid_t(&r->u.insert.author, r->u.insert.author_hex, sizeof r->u.insert.author_hex) == -1
|
|| strn_to_sid_t(&r->u.insert.author, r->u.insert.author_hex, sizeof r->u.insert.author_hex) == -1
|
||||||
)
|
)
|
||||||
return http_response_form_part(r, "Invalid", PART_AUTHOR, r->u.insert.author_hex, r->u.insert.author_hex_len);
|
return http_response_form_part(r, 400, "Invalid", PART_AUTHOR, r->u.insert.author_hex, r->u.insert.author_hex_len);
|
||||||
r->u.insert.received_author = 1;
|
r->u.insert.received_author = 1;
|
||||||
DEBUGF(rhizome, "received %s = %s", PART_AUTHOR, alloca_tohex_sid_t(r->u.insert.author));
|
DEBUGF(rhizome, "received %s = %s", PART_AUTHOR, alloca_tohex_sid_t(r->u.insert.author));
|
||||||
}
|
}
|
||||||
else if (r->u.insert.current_part == PART_SECRET) {
|
else if (r->u.insert.current_part == PART_SECRET) {
|
||||||
if (strn_to_rhizome_bsk_t(&r->u.insert.bundle_secret, r->u.insert.secret_text, r->u.insert.secret_text_len) == -1)
|
if (strn_to_rhizome_bsk_t(&r->u.insert.bundle_secret, r->u.insert.secret_text, r->u.insert.secret_text_len) == -1)
|
||||||
return http_response_form_part(r, "Invalid", PART_SECRET, r->u.insert.secret_text, r->u.insert.secret_text_len);
|
return http_response_form_part(r, 400, "Invalid", PART_SECRET, r->u.insert.secret_text, r->u.insert.secret_text_len);
|
||||||
r->u.insert.received_secret = 1;
|
r->u.insert.received_secret = 1;
|
||||||
DEBUGF(rhizome, "received %s = %s", PART_SECRET, alloca_tohex_rhizome_bk_t(r->u.insert.bundle_secret));
|
DEBUGF(rhizome, "received %s = %s", PART_SECRET, alloca_tohex_rhizome_bk_t(r->u.insert.bundle_secret));
|
||||||
}
|
}
|
||||||
else if (r->u.insert.current_part == PART_BUNDLEID) {
|
else if (r->u.insert.current_part == PART_BUNDLEID) {
|
||||||
if (strn_to_rhizome_bid_t(&r->bid, r->u.insert.bid_text, r->u.insert.bid_text_len) == -1)
|
if (strn_to_rhizome_bid_t(&r->bid, r->u.insert.bid_text, r->u.insert.bid_text_len) == -1)
|
||||||
return http_response_form_part(r, "Invalid", PART_BUNDLEID, r->u.insert.secret_text, r->u.insert.secret_text_len);
|
return http_response_form_part(r, 400, "Invalid", PART_BUNDLEID, r->u.insert.secret_text, r->u.insert.secret_text_len);
|
||||||
r->u.insert.received_bundleid = 1;
|
r->u.insert.received_bundleid = 1;
|
||||||
DEBUGF(rhizome, "received %s = %s", PART_BUNDLEID, alloca_tohex_rhizome_bid_t(r->bid));
|
DEBUGF(rhizome, "received %s = %s", PART_BUNDLEID, alloca_tohex_rhizome_bid_t(r->bid));
|
||||||
}
|
}
|
||||||
@ -640,9 +642,9 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
|||||||
{
|
{
|
||||||
httpd_request *r = (httpd_request *) hr;
|
httpd_request *r = (httpd_request *) hr;
|
||||||
if (!r->u.insert.received_manifest)
|
if (!r->u.insert.received_manifest)
|
||||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0);
|
||||||
if (!r->u.insert.received_payload)
|
if (!r->u.insert.received_payload)
|
||||||
return http_response_form_part(r, "Missing", PART_PAYLOAD, NULL, 0);
|
return http_response_form_part(r, 400, "Missing", PART_PAYLOAD, NULL, 0);
|
||||||
// Fill in the missing manifest fields and ensure payload and manifest are consistent.
|
// Fill in the missing manifest fields and ensure payload and manifest are consistent.
|
||||||
assert(r->manifest != NULL);
|
assert(r->manifest != NULL);
|
||||||
DEBUGF(rhizome, "r->payload_status=%d %s", r->payload_status, rhizome_payload_status_message(r->payload_status));
|
DEBUGF(rhizome, "r->payload_status=%d %s", r->payload_status, rhizome_payload_status_message(r->payload_status));
|
||||||
@ -693,7 +695,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
|||||||
}
|
}
|
||||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
||||||
return http_request_rhizome_response(r, 403, "Missing bundle secret", NULL); // Forbidden
|
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout
|
||||||
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
|
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
|
||||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
|
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
|
||||||
return http_request_rhizome_response(r, 202, "Bundle too big", NULL); // Accepted
|
return http_request_rhizome_response(r, 202, "Bundle too big", NULL); // Accepted
|
||||||
@ -717,7 +719,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
|
|||||||
}
|
}
|
||||||
if (!r->manifest->haveSecret) {
|
if (!r->manifest->haveSecret) {
|
||||||
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
|
||||||
return http_request_rhizome_response(r, 403, "Missing bundle secret", NULL); // Forbidden
|
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout
|
||||||
}
|
}
|
||||||
rhizome_manifest *mout = NULL;
|
rhizome_manifest *mout = NULL;
|
||||||
r->bundle_status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
|
r->bundle_status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
|
||||||
@ -918,7 +920,7 @@ int rhizome_response_content_init_payload(httpd_request *r, rhizome_manifest *m)
|
|||||||
case RHIZOME_PAYLOAD_STATUS_NEW:
|
case RHIZOME_PAYLOAD_STATUS_NEW:
|
||||||
return http_request_rhizome_response(r, 404, "Payload not found", NULL);
|
return http_request_rhizome_response(r, 404, "Payload not found", NULL);
|
||||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||||
return http_request_rhizome_response(r, 403, NULL, NULL); // Forbidden
|
return http_request_rhizome_response(r, 419, NULL, NULL); // Authentication Timeout
|
||||||
case RHIZOME_PAYLOAD_STATUS_ERROR:
|
case RHIZOME_PAYLOAD_STATUS_ERROR:
|
||||||
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
||||||
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
||||||
|
@ -813,6 +813,7 @@ enum rhizome_payload_status rhizome_finish_write(struct rhizome_write *write)
|
|||||||
}
|
}
|
||||||
if (sqlite_exec_void_retry(&retry, "COMMIT;", END) == -1)
|
if (sqlite_exec_void_retry(&retry, "COMMIT;", END) == -1)
|
||||||
goto dbfailure;
|
goto dbfailure;
|
||||||
|
// A test case in tests/rhizomeprotocol depends on this debug message:
|
||||||
DEBUGF(rhizome_store, "Stored file %s", alloca_tohex_rhizome_filehash_t(write->id));
|
DEBUGF(rhizome_store, "Stored file %s", alloca_tohex_rhizome_filehash_t(write->id));
|
||||||
}
|
}
|
||||||
write->blob_rowid = 0;
|
write->blob_rowid = 0;
|
||||||
|
@ -279,9 +279,9 @@ test_MeshmsListMessagesNoIdentity() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDX/$SIDA/messagelist.json"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDX/$SIDA/messagelist.json"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 419
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 419})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'meshms operation failed'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'identity locked'
|
||||||
assertJq http.body 'contains({"meshms_status_code": 2})'
|
assertJq http.body 'contains({"meshms_status_code": 2})'
|
||||||
assertJqGrep --ignore-case http.body '.meshms_status_message' 'identity.*unknown'
|
assertJqGrep --ignore-case http.body '.meshms_status_message' 'identity.*unknown'
|
||||||
}
|
}
|
||||||
@ -431,8 +431,8 @@ test_MeshmsSendMissingMessage() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*message.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*message.*form.*part'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -454,8 +454,8 @@ test_MeshmsSendDuplicateMessage() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*message.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*message.*form.*part'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -476,8 +476,8 @@ test_MeshmsSendMessageMissingContentType() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*content.*type'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*content.*type'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -498,8 +498,8 @@ test_MeshmsSendMessageUnsupportedContentType() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 415
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 415})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*content.*type'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*content.*type'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -520,8 +520,8 @@ test_MeshmsSendMessageMissingCharset() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*charset'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*charset'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -542,8 +542,8 @@ test_MeshmsSendMessageUnsupportedCharset() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 415
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 415})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*charset'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*charset'
|
||||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||||
assertStdoutLineCount '==' 2
|
assertStdoutLineCount '==' 2
|
||||||
@ -564,9 +564,9 @@ test_MeshmsSendNoIdentity() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDX/$SIDA/sendmessage"
|
"http://$addr_localhost:$PORTA/restful/meshms/$SIDX/$SIDA/sendmessage"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 419
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 419})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'meshms operation failed'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'identity locked'
|
||||||
assertJq http.body 'contains({"meshms_status_code": 2})'
|
assertJq http.body 'contains({"meshms_status_code": 2})'
|
||||||
assertJqGrep --ignore-case http.body '.meshms_status_message' 'identity.*unknown'
|
assertJqGrep --ignore-case http.body '.meshms_status_message' 'identity.*unknown'
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ test_RhizomePayloadDecryptedForeign() {
|
|||||||
--basic --user harry:potter \
|
--basic --user harry:potter \
|
||||||
"http://$addr_localhost:$PORTA/restful/rhizome/${BID[1]}/decrypted.bin"
|
"http://$addr_localhost:$PORTA/restful/rhizome/${BID[1]}/decrypted.bin"
|
||||||
tfw_cat http.headers$n decrypted.bin$n
|
tfw_cat http.headers$n decrypted.bin$n
|
||||||
assertStdoutIs 403
|
assertStdoutIs 419
|
||||||
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Code: 1$CR\$"
|
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Code: 1$CR\$"
|
||||||
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Message: .*bundle already in store.*$CR\$"
|
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Message: .*bundle already in store.*$CR\$"
|
||||||
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Code: 5$CR\$"
|
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Code: 5$CR\$"
|
||||||
@ -648,8 +648,8 @@ test_RhizomeInsert() {
|
|||||||
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Code: 1$CR\$"
|
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Code: 1$CR\$"
|
||||||
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Message: .*payload new to store.*$CR\$"
|
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Message: .*payload new to store.*$CR\$"
|
||||||
else
|
else
|
||||||
assertStdoutIs 403
|
assertStdoutIs 419
|
||||||
assertJq nfile$n.manifest 'contains({"http_status_code": 403})'
|
assertJq nfile$n.manifest 'contains({"http_status_code": 419})'
|
||||||
assertJqGrep --ignore-case nfile$n.manifest '.http_status_message' "missing bundle secret"
|
assertJqGrep --ignore-case nfile$n.manifest '.http_status_message' "missing bundle secret"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@ -881,8 +881,8 @@ test_RhizomeInsertMissingManifest() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -904,8 +904,8 @@ test_RhizomeInsertIncorrectManifestType() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 415
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 415})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported content-type.*manifest.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported content-type.*manifest.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -927,8 +927,8 @@ test_RhizomeInsertIncorrectManifestFormat() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 415
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 415})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*format.*manifest.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*format.*manifest.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -953,8 +953,8 @@ test_RhizomeInsertDuplicateManifest() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*manifest.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*manifest.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -1001,8 +1001,8 @@ test_RhizomeInsertMissingPayload() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*payload.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*payload.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -1027,8 +1027,8 @@ test_RhizomeInsertDuplicatePayload() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*payload.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'duplicate.*payload.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -1051,8 +1051,8 @@ test_RhizomeInsertPartOrder() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'missing.*manifest.*form.*part'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
assert_rhizome_list
|
assert_rhizome_list
|
||||||
@ -1076,8 +1076,8 @@ test_RhizomeInsertPartUnsupported() {
|
|||||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||||
tfw_cat http.header http.body
|
tfw_cat http.header http.body
|
||||||
assertExitStatus == 0
|
assertExitStatus == 0
|
||||||
assertStdoutIs 403
|
assertStdoutIs 400
|
||||||
assertJq http.body 'contains({"http_status_code": 403})'
|
assertJq http.body 'contains({"http_status_code": 400})'
|
||||||
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*form.*part'
|
assertJqGrep --ignore-case http.body '.http_status_message' 'unsupported.*form.*part'
|
||||||
assertJqGrep http.body '.http_status_message' 'happyhappy'
|
assertJqGrep http.body '.http_status_message' 'happyhappy'
|
||||||
executeOk_servald rhizome list
|
executeOk_servald rhizome list
|
||||||
|
Loading…
Reference in New Issue
Block a user