Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ A breaking change will get clearly marked in this log.

## Unreleased

### Fixed

* X-App-Name and X-App-Version headers are now included when using `CallBuilder.stream()`
Comment thread
Ryang-21 marked this conversation as resolved.
Outdated


## [v14.4.3](https://github.com/stellar/js-stellar-sdk/compare/v14.4.2...v14.4.3)

Expand Down
21 changes: 21 additions & 0 deletions src/horizon/call_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,27 @@ export class CallBuilder<
this.url.setQuery("X-Client-Name", "js-stellar-sdk");
this.url.setQuery("X-Client-Version", version);

// Extract custom app headers from httpClient defaults and add as query params
// (EventSource doesn't support custom headers, so we use query params)
const { headers } = this.httpClient.defaults;
if (headers) {
const headerNames = ["X-App-Name", "X-App-Version"];
headerNames.forEach((name) => {
let value: string | undefined;
if (headers instanceof Headers) {
value = headers.get(name) ?? undefined;
Comment thread
Shaptic marked this conversation as resolved.
} else if (Array.isArray(headers)) {
const entry = headers.find(([key]) => key === name);
value = entry?.[1];
} else {
value = headers[name];
}
if (value) {
this.url.setQuery(name, value);
}
});
}

// EventSource object
let es: EventSource;
// timeout is the id of the timeout to be triggered if there were no new messages
Expand Down
82 changes: 82 additions & 0 deletions test/integration/client_headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,86 @@ describe("integration tests: client headers", () => {
.operations()
.call();
});

it("sends appName and appVersion via headers for HTTP requests", async () => {
let server: http.Server;

const requestHandler = (
request: http.IncomingMessage,
response: http.ServerResponse,
) => {
expect(request.headers["x-app-name"]).toBe("my-app");
expect(request.headers["x-app-version"]).toBe("1.0.0");
response.end();
server.close();
};

server = http.createServer(requestHandler);

await new Promise<void>((resolve, reject) => {
server.listen(port, (err?: Error) => {
if (err) {
reject(err);
return;
}
resolve();
});
});

await new Horizon.Server(`http://localhost:${port}`, {
appName: "my-app",
appVersion: "1.0.0",
allowHttp: true,
})
.operations()
.call();
});

it("sends appName and appVersion via query params when streaming", async () => {
let server: http.Server;
let closeStream: () => void;

const requestHandler = (
request: http.IncomingMessage,
response: http.ServerResponse,
) => {
const query = url.parse(request.url!, true).query;
expect(query["X-App-Name"]).toBe("my-app");
expect(query["X-App-Version"]).toBe("1.0.0");

response.writeHead(200, {
"Content-Type": "text/event-stream",
});
response.write("retry: 10\nevent: close\ndata: byebye\n\n");
response.end();

server.close(() => {
closeStream();
});
};

server = http.createServer(requestHandler);

await new Promise<void>((resolve, reject) => {
server.listen(port, (err?: Error) => {
if (err) {
reject(err);
return;
}
resolve();
});
});

closeStream = new Horizon.Server(`http://localhost:${port}`, {
appName: "my-app",
appVersion: "1.0.0",
allowHttp: true,
})
.operations()
.stream({
onerror: (err) => {
throw err;
},
});
});
});
3 changes: 2 additions & 1 deletion test/unit/call_builders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import { describe, it, expect } from "vitest";
import URI from "urijs";
import { CallBuilder } from "../../lib/horizon/call_builder";
import { httpClient } from "../test-utils/stellar-sdk-import";

describe("CallBuilder functions", () => {
it("doesn't mutate the constructor passed url argument (it clones it instead)", () => {
const arg = URI("https://onedom.ain/");
const builder = new CallBuilder(arg);
const builder = new CallBuilder(arg, httpClient);
builder["url"].segment("one_segment");
builder["checkFilter"]();

Expand Down
Loading