How do you call http_request on localhost?

From the recent challenge to find the secret, i am wondering how we can call the http_request in local deployment?

public query func http_request() : async HttpResponse {
return {
status_code = 200;
headers = [(“content-type”, “text/plain”)];
body = T.encodeUtf8 (
“This is nomeata’s capture-the-ic-token canister.\n” #
“See GitHub - nomeata/capture-the-ic-token: Hack the canister, get the token for details.\n” #
“\n” #
"My current cycle balance: " # debug_show (ExperimentalCycles.balance()) # “\n” #
"Secret loaded from random tape: " # debug_show (O.isSome(secret)) # “\n” #
"Successful calls to set_certified_data: " # debug_show successful_calls # “\n” #
"Unsuccessful calls to set_certified_data: " # debug_show unsuccessful_calls # “\n”
)
}
};

One way is to use icx-proxy to forward HTTP requests.

5 Likes

Can you clarify how you would call the icx-proxy locally? For example, let’s say I have multiple canisters deployed on my local replica, and only one of them has implemented http_request. How do I target that canister locally using HTTP?

Typically in prod, you would make an HTTP request to https://<canister-id>.raw.ic0.app, where canister-id is the ID of the canister that implemented http_request.

However when testing locally, the domain in the URL is the address that the local icx-proxy is listening on, something like https://localhost:8080. Where would you specify the canister ID in this case?

The way I did it for now was to edit /etc/hosts to add an entry like:

127.0.0.1 myproject.localhost

Then start icx-proxy like this:

icx-proxy --fetch-root-key --address 127.0.0.1:8453 --dns-alias myproject.localhost:rrkah-fqaaa-aaaaa-aaaaq-cai -v -v

That way I can make HTTP requests to http://myproject.localhost:8453. You can pick a different host name and port.

4 Likes

Hmm, don’t you also need to tell icx-proxy where the local replica is listening on? Like with a flag --replica http://localhost:$(cat .dfx/replica-configuration/replica-1.port)

1 Like

I haven’t been doing that locally so I guess the default value matches the local replica.

2 Likes

Holy crap, it worked. Thanks a bunch!

Actually, you can skip the /etc/hosts step and use the IP address directly in the --dns-alias flag, e.g. --dns-alias 127.0.0.1:<canister_id>.

1 Like

Yes, sorry I didn’t make that clear.

Also, you may find this useful: feat: upgrade HTTP request calls from query to update upon canister's request by paulyoung · Pull Request #19

How do you configure it on the main network,
I got an error when I replaced dns-alias with https://ic0.app
Error: Text must be a base 32 string.

I think you need to make requests to https://your-canister-id.ic0.app

I get the same error when I request this URL with cmd
icx-proxy --debug --address 127.0.0.1:8453 --dns-alias https://d4svy-5yaaa-aaaai-qam4q-cai.ic0.app

Once things are deployed to the network you wouldn’t need to use icx-proxy and would make HTTP requests directly to https://your-canister-id.ic0.app instead

Got it - thanks for the explanation.

@ericswanson Since icx-proxy is now the default server used by dfx start, it would be helpful if dfx start could let users configure a custom dns_alias flag, which would then get passed to icx-proxy when executing that binary.

Otherwise, developers can’t test that their custom http_request code works with dfx start. This means no fetching images using HTTP locally.

The current workaround is to pass a flag like --dns-alias 127.0.0.1:rrkah-fqaaa-aaaaa-aaaaq-cai directly to icx-proxy, as suggested by @paulyoung earlier in this thread.

1 Like

I’m guessing this would involve adding to the options for the start command:

and then passing that along to the proxy config:

Sorry, I must be missing something. Using your example (with 127.0.0.1 in the --dns-flag) my browser returns unable to fetch root key when I navigate to http://127.0.0.1:8453/. Is this expected?

My expectation is that in your example, the backend canister rrkah-fqaaa-aaaaa-aaaaq-cai would return a response, given this query function:

public query func http_request_text(request : HttpRequest) : async HttpResponse {
    if ((request.method, request.url) == ("GET", "/")) {
        return {
            status_code = 200;
            headers = [("content-type", "text-plain")];
            body = "this works,  but it's uncertified"
        };
    } else {
        return {
            status_code = 404;
            headers = [("content-type", "text-plain")];
            body = "404 not found";
        };
    };
};

I run icx-proxy with: icx-proxy --fetch-root-key --address 127.0.0.1:8453 --dns-alias 127.0.0.1:rrkah-fqaaa-aaaaa-aaaaq-cai -v

The backend canister via Candid can be reached via: http://127.0.0.1:4943/?canisterId=r7inp-6aaaa-aaaaa-aaabq-cai&id=rrkah-fqaaa-aaaaa-aaaaq-cai

Are you calling fetchRootKey?

By default, the agent is configured to talk to the main Internet Computer, and verifies responses using a hard-coded public key.

This function will instruct the agent to ask the endpoint for its public key, and use that instead. This is required when talking to a local test instance, for example.

Only use this when you are not talking to the main Internet Computer, otherwise you are prone to man-in-the-middle attacks! Do not call this function by default.

I’m sorry Paul, this was unneccessary. I forgot to set the --replica flag. To sum it up for anyone else looking this up, this is the whole command:

icx-proxy --fetch-root-key --address 127.0.0.1:8453 --dns-alias 127.0.0.1:rrkah-fqaaa-aaaaa-aaaaq-cai --replica http://localhost:4943 -v

Run this after dfx start. It will map the canister with ID rrkah-fqaaa-aaaaa-aaaaq-cai to http://127.0.0.1:8453 when the local replica is running under port 4943 (which I think is the default) and thereby allow http requests to a backend canister in local development.

1 Like