Networking
The HttpClient interface, its interceptors, its rate limiter, and the rules for page images.
Suwatte has two network stacks. HttpClient is the current one. Turn it on with the
"use httpclient" directive.
HttpClient
export default class Target {
client = new HttpClient({
baseURL: "https://example.org",
headers: { "User-Agent": "..." },
rateLimit: { permits: 3, period: 1 },
});
async getContent(contentId: string) {
const response = await this.client.get(`/series/${contentId}`);
return this.parseContent(await response.json());
}
}
The methods
Use get(url, config), head(url, config) and delete(url, config) for a request with no body.
Use post(url, body, config), put(url, body, config) and patch(url, body, config) for a
request with a body. The body is the second argument.
Each method returns an HttpResponse.
The response
| Member | Notes |
|---|---|
status |
The numeric status code. |
ok |
True for 200 to 299. |
headers |
The headers. Use get(name) to read one. |
await text() |
The body as a string. |
await json() |
The body as parsed JSON. |
await bytes() and await arrayBuffer() |
The body as data. |
A status code that is not 2xx is not an error. The request completes. Read ok yourself, and
throw if you want the request to fail.
Interceptors
client.interceptors.request.use(async (request) => {
request.headers.set("Authorization", `Bearer ${await this.token()}`);
return request;
});
client.interceptors.response.use(async (response) => {
return response;
});
A request interceptor must return the HttpRequest that it got. If it returns something
different, the client throws a TypeError. This stops an interceptor from a quiet exchange of one
request for another.
The rate limiter
Set rateLimit: { permits, period }. This gives you permits requests for each period in
seconds. A request waits in a queue when there are no permits. It does not fail.
The limiter also slows down on its own when the site returns a throttle status code or a throttle header. A site that starts to return 429 makes the client wait, and you write no code for it.
The errors
HttpClient throws before it sends a request in two conditions:
- You are in WebKit.
HttpClientneeds JavaScriptCore. The message tells you to remove"use webkit"or to use the olderNetworkClient. - The directive is missing. The runtime rejects the call. If you are sure that the directive is in your file, look at the file that the build emitted. A banner can move the directive past 512 bytes.
A network failure throws a NetworkError. It holds the request, and the response if one exists.
Page images
The image loader of the app gets page images. It uses the imageReferer value from your
configuration.
Set useClientForImageRequests: true when that is not enough. An example is a signed address, a
session cookie, or a header that the loader does not send. Suwatte then gets each page image with
your client.
The bootstrap checks three rules for this option:
- The JavaScriptCore environment.
- The
"use httpclient"directive. - A
clientproperty that is anHttpClientinstance.
Two other methods give you control without the whole transport:
willRequestImage(url)changes a request before it goes out.redrawImage(...)changes an image after the app gets it. Use it for a source that sends pages in pieces.
Cloudflare
Set cloudflareResolutionURL in your configuration. The app opens that page when a challenge
appears. Suwatte then uses the cookies for the requests of your source.
The older stack
A source with no "use httpclient" directive uses NetworkClient. It supports
transformRequest and transformResponse.
NetworkClient still works, and an existing source does not need to change. Use HttpClient for
a new source.