1
0
Fork 0
arangodb/Documentation/Books/HTTP/general.md

451 lines
21 KiB
Markdown

---
layout: default
description: ArangoDB exposes its API via HTTP, making the server accessible easily witha variety of clients and tools (e
---
General HTTP Request Handling in ArangoDB
=========================================
Protocol
--------
ArangoDB exposes its API via HTTP, making the server accessible easily with
a variety of clients and tools (e.g. browsers, curl, telnet). The communication
can optionally be SSL-encrypted.
ArangoDB uses the standard HTTP methods (e.g. *GET*, *POST*, *PUT*, *DELETE*) plus
the *PATCH* method described in [RFC 5789](http://tools.ietf.org/html/rfc5789){:target="_blank"}.
Most server APIs expect clients to send any payload data in [JSON](http://www.json.org){:target="_blank"}
format. Details on the expected format and JSON attributes can be found in the
documentation of the individual server methods.
Clients sending requests to ArangoDB must use either HTTP 1.0 or HTTP 1.1.
Other HTTP versions are not supported by ArangoDB and any attempt to send
a different HTTP version signature will result in the server responding with
an HTTP 505 (HTTP version not supported) error.
ArangoDB will always respond to client requests with HTTP 1.1. Clients
should therefore support HTTP version 1.1.
Clients are required to include the *Content-Length* HTTP header with the
correct content length in every request that can have a body (e.g. *POST*,
*PUT* or *PATCH*) request. ArangoDB will not process requests without a
*Content-Length* header - thus chunked transfer encoding for POST-documents
is not supported.
HTTP Keep-Alive
---------------
ArangoDB supports HTTP keep-alive. If the client does not send a *Connection*
header in its request, and the client uses HTTP version 1.1, ArangoDB will assume
the client wants to keep alive the connection.
If clients do not wish to use the keep-alive feature, they should
explicitly indicate that by sending a *Connection: Close* HTTP header in
the request.
ArangoDB will close connections automatically for clients that send requests
using HTTP 1.0, except if they send an *Connection: Keep-Alive* header.
The default Keep-Alive timeout can be specified at server start using the
*--http.keep-alive-timeout* parameter.
Establishing TCP connections is expensive, since it takes several ping pongs
between the communication parties. Therefore you can use connection keepalive
to send several HTTP request over one TCP-connection;
Each request is treated independently by definition. You can use this feature
to build up a so called *connection pool* with several established
connections in your client application, and dynamically re-use
one of those then idle connections for subsequent requests.
Blocking vs. Non-blocking HTTP Requests
---------------------------------------
ArangoDB supports both blocking and non-blocking HTTP requests.
ArangoDB is a multi-threaded server, allowing the processing of multiple
client requests at the same time. Request/response handling and the actual
work are performed on the server in parallel by multiple worker threads.
Still, clients need to wait for their requests to be processed by the server,
and thus keep one connection of a pool occupied.
By default, the server will fully process an incoming request and then return
the result to the client when the operation is finished. The client must
wait for the server's HTTP response before it can send additional requests over
the same connection. For clients that are single-threaded and/or are
blocking on I/O themselves, waiting idle for the server response may be
non-optimal.
To reduce blocking on the client side, ArangoDB offers a generic mechanism for
non-blocking, asynchronous execution: clients can add the
HTTP header *x-arango-async: true* to any of their requests, marking
them as to be executed asynchronously on the server. ArangoDB will put such
requests into an in-memory task queue and return an *HTTP 202* (accepted)
response to the client instantly and thus finish this HTTP-request.
The server will execute the tasks from the queue asynchronously as fast
as possible, while clients can continue to do other work.
If the server queue is full (i.e. contains as many tasks as specified by the
option ["--server.maximal-queue-size"](../programs-arangod-options.html#arangodb-server-options)),
then the request will be rejected instantly with an *HTTP 500* (internal
server error) response.
Asynchronous execution decouples the request/response handling from the actual
work to be performed, allowing fast server responses and greatly reducing wait
time for clients. Overall this allows for much higher throughput than if
clients would always wait for the server's response.
Keep in mind that the asynchronous execution is just "fire and forget".
Clients will get any of their asynchronous requests answered with a generic
HTTP 202 response. At the time the server sends this response, it does not
know whether the requested operation can be carried out successfully (the
actual operation execution will happen at some later point). Clients therefore
cannot make a decision based on the server response and must rely on their
requests being valid and processable by the server.
Additionally, the server's asynchronous task queue is an in-memory data
structure, meaning not-yet processed tasks from the queue might be lost in
case of a crash. Clients should therefore not use the asynchronous feature
when they have strict durability requirements or if they rely on the immediate
result of the request they send.
For details on the subsequent processing
[read on under Async Result handling](async-results-management.html).
Authentication
--------------
Client authentication can be achieved by using the *Authorization* HTTP header in
client requests. ArangoDB supports authentication via HTTP Basic or JWT.
Authentication is turned on by default for all internal database APIs but turned off for custom Foxx apps.
To toggle authentication for incoming requests to the internal database APIs, use the option
[--server.authentication](../programs-arangod-server.html#enabledisable-authentication).
This option is turned on by default so authentication is required for the database APIs.
Please note that requests using the HTTP OPTIONS method will be answered by
ArangoDB in any case, even if no authentication data is sent by the client or if
the authentication data is wrong. This is required for handling CORS preflight
requests (see [Cross Origin Resource Sharing requests](#cross-origin-resource-sharing-cors-requests)).
The response to an HTTP OPTIONS request will be generic and not expose any private data.
There is an additional option to control authentication for custom Foxx apps. The option
[--server.authentication-system-only](../programs-arangod-server.html#enabledisable-authentication-for-system-api-requests-only)
controls whether authentication is required only for requests to the internal database APIs and the admin interface.
It is turned on by default, meaning that other APIs (this includes custom Foxx apps) do not require authentication.
The default values allow exposing a public custom Foxx API built with ArangoDB to the outside
world without the need for HTTP authentication, but still protecting the usage of the
internal database APIs (i.e. */_api/*, */_admin/*) with HTTP authentication.
If the server is started with the *--server.authentication-system-only* option set
to *false*, all incoming requests will need HTTP authentication if the server is configured
to require HTTP authentication (i.e. *--server.authentication true*).
Setting the option to *true* will make the server require authentication only for requests to the
internal database APIs and will allow unauthenticated requests to all other URLs.
Here's a short summary:
* `--server.authentication true --server.authentication-system-only true`: this will require
authentication for all requests to the internal database APIs but not custom Foxx apps.
This is the default setting.
* `--server.authentication true --server.authentication-system-only false`: this will require
authentication for all requests (including custom Foxx apps).
* `--server.authentication false`: authentication disabled for all requests
Whenever authentication is required and the client has not yet authenticated,
ArangoDB will return *HTTP 401* (Unauthorized). It will also send the *WWW-Authenticate*
response header, indicating that the client should prompt the user for username and
password if supported. If the client is a browser, then sending back this header will
normally trigger the display of the browser-side HTTP authentication dialog.
As showing the browser HTTP authentication dialog is undesired in AJAX requests,
ArangoDB can be told to not send the *WWW-Authenticate* header back to the client.
Whenever a client sends the *X-Omit-WWW-Authenticate* HTTP header (with an arbitrary value)
to ArangoDB, ArangoDB will only send status code 401, but no *WWW-Authenticate* header.
This allows clients to implement credentials handling and bypassing the browser's
built-in dialog.
### Authentication via JWT
ArangoDB uses a standard JWT based authentication method.
To authenticate via JWT you must first obtain a JWT token with a signature generated via HMAC with SHA-256.
The secret may either be set using `--server.jwt-secret` or will be randomly generated upon server startup.
For more information on JWT please consult RFC7519 and [jwt.io](https://jwt.io){:target="_blank"}.
#### User JWT-Token
To authenticate with a specific user you need to supply a JWT token containing
the _preferred_username_ field with the username.
You can either let ArangoDB generate this token for you via an API call
or you can generate it yourself (only if you know the JWT secret).
ArangoDB offers a REST API to generate user tokens for you if you know the username and password.
To do so send a POST request to
*/_open/auth*
containing *username* and *password* JSON-encoded like so:
{"username":"root","password":"rootPassword"}
Upon success the endpoint will return a **200 OK** and an answer containing the JWT in a JSON-
encoded object like so:
```
{"jwt":"eyJhbGciOiJIUzI1NiI..x6EfI"}
```
This JWT should then be used within the Authorization HTTP header in subsequent requests:
```
Authorization: bearer eyJhbGciOiJIUzI1NiI..x6EfI
```
Please note that the JWT will expire after 1 month and needs to be updated. We encode the expiration
date of the JWT token in the _exp_ field in unix time.
Please note that all JWT tokens must contain the _iss_ field with string value `arangodb`.
As an example the decoded JWT body would look like this:
```json
{
"exp": 1540381557,
"iat": 1537789.55727901,
"iss": "arangodb",
"preferred_username": "root"
}
```
#### Superuser JWT-Token
To access specific internal APIs as well as Agency and DBServer instances a token generated via `/open/auth` is not
good enough. For these special APIs you will need to generate a special JWT token which grants superuser access.
Note that using superuser access for normal database operations is **NOT advised**.
_Note_: It is only possible to generate this JWT token with the knowledge of the JWT secret.
For your convenience it is possible to generate this token via the [ArangoDB starter CLI](../programs-starter-security.html#using-authentication-tokens).
Should you wish to generate the JWT token yourself with a tool of your choice, you need to include the correct body.
The body must contain the _iss_ field with string value `arangodb` and the `server_id` field with an arbitrary string identifier:
```json
{
"exp": 1537900279,
"iat": 1537800279,
"iss": "arangodb",
"server_id": "myclient"
}
```
For example to generate a token via the [jwtgen tool](https://www.npmjs.com/package/jwtgen){:target="_blank"} (note the lifetime of one hour):
```
jwtgen -s <my-secret> -e 3600 -v -a "HS256" -c 'iss=arangodb' -c 'server_id=myclient'
curl -v -H "Authorization: bearer $(jwtgen -s <my-secret> -e 3600 -a "HS256" -c 'iss=arangodb' -c 'server_id=myclient')" http://<database-ip>:8529/_api/version
```
Error Handling
--------------
The following should be noted about how ArangoDB handles client errors in its
HTTP layer:
* client requests using an HTTP version signature different than *HTTP/1.0* or
*HTTP/1.1* will get an *HTTP 505* (HTTP version not supported) error in return.
* ArangoDB will reject client requests with a negative value in the
*Content-Length* request header with *HTTP 411* (Length Required).
* ArangoDB doesn't support POST with *transfer-encoding: chunked* which forbids
the *Content-Length* header above.
* the maximum URL length accepted by ArangoDB is 16K. Incoming requests with
longer URLs will be rejected with an *HTTP 414* (Request-URI too long) error.
* if the client sends a *Content-Length* header with a value bigger than 0 for
an HTTP GET, HEAD, or DELETE request, ArangoDB will process the request, but
will write a warning to its log file.
* when the client sends a *Content-Length* header that has a value that is lower
than the actual size of the body sent, ArangoDB will respond with *HTTP 400*
(Bad Request).
* if clients send a *Content-Length* value bigger than the actual size of the
body of the request, ArangoDB will wait for about 90 seconds for the client to
complete its request. If the client does not send the remaining body data
within this time, ArangoDB will close the connection. Clients should avoid
sending such malformed requests as this will block one tcp connection,
and may lead to a temporary file descriptor leak.
* when clients send a body or a *Content-Length* value bigger than the maximum
allowed value (512 MB), ArangoDB will respond with *HTTP 413* (Request Entity
Too Large).
* if the overall length of the HTTP headers a client sends for one request
exceeds the maximum allowed size (1 MB), the server will fail with *HTTP 431*
(Request Header Fields Too Large).
* if clients request an HTTP method that is not supported by the server, ArangoDB
will return with *HTTP 405* (Method Not Allowed). ArangoDB offers general
support for the following HTTP methods:
* GET
* POST
* PUT
* DELETE
* HEAD
* PATCH
* OPTIONS
Please note that not all server actions allow using all of these HTTP methods.
You should look up the supported methods for each method you intend to use
in the manual.
Requests using any other HTTP method (such as for example CONNECT, TRACE etc.)
will be rejected by ArangoDB as mentioned before.
Cross-Origin Resource Sharing (CORS) requests
---------------------------------------------
ArangoDB will automatically handle CORS requests as follows:
### Preflight
When a browser is told to make a cross-origin request that includes explicit
headers, credentials or uses HTTP methods other than `GET` or `POST`, it will
first perform a so-called preflight request using the `OPTIONS` method.
ArangoDB will respond to `OPTIONS` requests with an HTTP 200 status response
with an empty body. Since preflight requests are not expected to include or
even indicate the presence of authentication credentials even when they will
be present in the actual request, ArangoDB does not enforce authentication for
`OPTIONS` requests even when authentication is enabled.
ArangoDB will set the following headers in the response:
* `access-control-allow-credentials`: will be set to `false` by default.
For details on when it will be set to `true` see the next section on cookies.
* `access-control-allow-headers`: will be set to the exact value of the
request's `access-control-request-headers` header or omitted if no such
header was sent in the request.
* `access-control-allow-methods`: will be set to a list of all supported HTTP
headers regardless of the target endpoint. In other words that a method is
listed in this header does not guarantee that it will be supported by the
endpoint in the actual request.
* `access-control-allow-origin`: will be set to the exact value of the
request's `origin` header.
* `access-control-expose-headers`: will be set to a list of response headers used
by the ArangoDB HTTP API.
* `access-control-max-age`: will be set to an implementation-specific value.
### Actual request
If a request using any other HTTP method than `OPTIONS` includes an `origin` header,
ArangoDB will add the following headers to the response:
* `access-control-allow-credentials`: will be set to `false` by default.
For details on when it will be set to `true` see the next section on cookies.
* `access-control-allow-origin`: will be set to the exact value of the
request's `origin` header.
* `access-control-expose-headers`: will be set to a list of response headers used
by the ArangoDB HTTP API.
When making CORS requests to endpoints of Foxx services, the value of the
`access-control-expose-headers` header will instead be set to a list of
response headers used in the response itself (but not including the
`access-control-` headers). Note that [Foxx services may override this behavior](../foxx-guides-browser.html#cross-origin-resource-sharing-cors).
### Cookies and authentication
In order for the client to be allowed to correctly provide authentication
credentials or handle cookies, ArangoDB needs to set the
`access-control-allow-credentials` response header to `true` instead of `false`.
ArangoDB will automatically set this header to `true` if the value of the
request's `origin` header matches a trusted origin in the `http.trusted-origin`
configuration option. To make ArangoDB trust a certain origin, you can provide
a startup option when running `arangod` like this:
`--http.trusted-origin "http://localhost:8529"`
To specify multiple trusted origins, the option can be specified multiple times.
Alternatively you can use the special value `"*"` to trust any origin:
`--http.trusted-origin "*"`
Note that browsers will not actually include credentials or cookies in cross-origin
requests unless explicitly told to do so:
* When using the Fetch API you need to set the
[`credentials` option to `include`](https://fetch.spec.whatwg.org/#cors-protocol-and-credentials){:target="_blank"}.
```js
fetch("./", { credentials:"include" }).then(/* … */)
```
* When using `XMLHttpRequest` you need to set the
[`withCredentials` option to `true`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials){:target="_blank"}.
```js
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
```
* When using jQuery you need to set the `xhrFields` option:
```js
$.ajax({
url: 'https://example.com',
xhrFields: {
withCredentials: true
}
});
```
HTTP method overriding
----------------------
ArangoDB provides a startup option *--http.allow-method-override*.
This option can be set to allow overriding the HTTP request method (e.g. GET, POST,
PUT, DELETE, PATCH) of a request using one of the following custom HTTP headers:
* *x-http-method-override*
* *x-http-method*
* *x-method-override*
This allows using HTTP clients that do not support all "common" HTTP methods such as
PUT, PATCH and DELETE. It also allows bypassing proxies and tools that would otherwise
just let certain types of requests (e.g. GET and POST) pass through.
Enabling this option may impose a security risk, so it should only be used in very
controlled environments. Thus the default value for this option is *false* (no method
overriding allowed). You need to enable it explicitly if you want to use this
feature.
Load-balancer support
---------------------
When running in cluster mode, ArangoDB exposes some APIs which store request
state data on specific coordinator nodes, and thus subsequent requests which
require access to this state must be served by the coordinator node which owns
this state data. In order to support function behind a load-balancer, ArangoDB
can transparently forward requests within the cluster to the correct node. If a
request is forwarded, the response will contain the following custom HTTP header
whose value will be the ID of the node which actually answered the request:
* *x-arango-request-forwarded-to*
The following APIs may use request forwarding:
* `/_api/cursor`
* `/_api/job`
* `/_api/tasks`
Note: since forwarding such requests require an additional cluster-internal HTTP
request, they should be avoided when possible for best performance. Typically
this is accomplished either by directing the requests to the correct coordinator
at a client-level or by enabling request "stickiness" on a load balancer. Since
these approaches are not always possible in a given environment, we support the
request forwarding as a fall-back solution.
Note: some endpoints which return "global" data, such as `GET /_api/tasks` will
only return data corresponding to the server on which the request is executed.
These endpoints will generally not work well with load-balancers.