-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #54 from golemcloud/c-example-http
c-example-http
- Loading branch information
Showing
4 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
build: compile | ||
wasm-tools component new component_name.module.wasm -o component_name.wasm --adapt adapters/tier1/wasi_snapshot_preview1.wasm | ||
|
||
bindings: | ||
wit-bindgen c --autodrop-borrows yes --out-dir component_name ./wit | ||
|
||
compile: bindings | ||
${WASI_SDK}/bin/clang --sysroot ${WASI_SDK}/share/wasi-sysroot main.c component_name/component_name.c component_name/component_name_component_type.o -o component_name.module.wasm | ||
|
||
clean: | ||
rm -rf component_name | ||
rm *.wasm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <time.h> | ||
|
||
#include "component_name/component_name.h" | ||
|
||
int32_t main(void) { | ||
return 0; | ||
} | ||
|
||
// Component state | ||
static uint64_t total = 0; | ||
|
||
// Implementation of the exported functions. | ||
// See component_name.h for the generated function signatures. | ||
void exports_pack_name_api_add(uint64_t value) { | ||
total += value; | ||
} | ||
|
||
uint64_t exports_pack_name_api_get() { | ||
return total; | ||
} | ||
|
||
|
||
void set_string(c_example_http_string_t* ret, const char* str) { | ||
ret->ptr = (uint8_t*)str; | ||
ret->len = strlen(str); | ||
} | ||
|
||
void set_string_field(wasi_http_types_field_value_t* ret, const char* str) { | ||
ret->ptr = (uint8_t*)str; | ||
ret->len = strlen(str); | ||
} | ||
|
||
#define log(...) { fprintf (stderr, __VA_ARGS__); fflush(stderr); } | ||
|
||
void exports_pack_name_api_send(c_example_http_string_t *ret) { | ||
log("Setting up the outgoing request\n"); | ||
// Setting up the outgoing request | ||
wasi_http_types_own_fields_t headers; | ||
wasi_http_types_header_error_t headers_err; | ||
c_example_http_list_tuple2_field_key_field_value_t entries; | ||
entries.ptr = malloc(2 * sizeof(c_example_http_tuple2_field_key_field_value_t)); | ||
entries.len = 2; | ||
set_string(&entries.ptr[0].f0, "Content-Type"); | ||
set_string_field(&entries.ptr[0].f1, "application/json"); | ||
set_string(&entries.ptr[1].f0, "Accept"); | ||
set_string_field(&entries.ptr[1].f1, "application/json"); | ||
|
||
if (!wasi_http_types_static_fields_from_list(&entries, &headers, &headers_err)) { | ||
set_string(ret, "Failed to create header list"); | ||
return; | ||
} | ||
|
||
log("Created the header list\n"); | ||
|
||
wasi_http_types_own_outgoing_request_t request = wasi_http_types_constructor_outgoing_request(headers); | ||
|
||
log("Created the request\n"); | ||
|
||
wasi_http_types_method_t method; | ||
method.tag = WASI_HTTP_TYPES_METHOD_POST; | ||
if (!wasi_http_types_method_outgoing_request_set_method( | ||
wasi_http_types_borrow_outgoing_request(request), | ||
&method | ||
)) { | ||
set_string(ret, "Failed to set method"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
c_example_http_string_t path; | ||
set_string(&path, "/post"); | ||
if (!wasi_http_types_method_outgoing_request_set_path_with_query( | ||
wasi_http_types_borrow_outgoing_request(request), | ||
&path | ||
)) { | ||
set_string(ret, "Failed to set path"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
wasi_http_types_scheme_t scheme; | ||
scheme.tag = WASI_HTTP_TYPES_SCHEME_HTTPS; | ||
if (!wasi_http_types_method_outgoing_request_set_scheme( | ||
wasi_http_types_borrow_outgoing_request(request), | ||
&scheme | ||
)) { | ||
set_string(ret, "Failed to set scheme"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
c_example_http_string_t authority; | ||
set_string(&authority, "httpbin.org"); | ||
if (!wasi_http_types_method_outgoing_request_set_authority( | ||
wasi_http_types_borrow_outgoing_request(request), | ||
&authority | ||
)) { | ||
set_string(ret, "Failed to set authority"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
wasi_http_types_own_outgoing_body_t out_body; | ||
if (!wasi_http_types_method_outgoing_request_body(wasi_http_types_borrow_outgoing_request(request), &out_body)) { | ||
set_string(ret, "Failed to get outgoing body"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
wasi_http_types_own_output_stream_t out_body_stream; | ||
if (!wasi_http_types_method_outgoing_body_write(wasi_http_types_borrow_outgoing_body(out_body), &out_body_stream)) { | ||
set_string(ret, "Failed to get outgoing body stream"); | ||
wasi_http_types_outgoing_body_drop_own(out_body); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
log("Writing the outgoing request stream\n"); | ||
|
||
wasi_io_streams_stream_error_t stream_err; | ||
c_example_http_list_u8_t body_data; | ||
body_data.ptr = malloc(256); | ||
sprintf((char*)body_data.ptr, "{ \"count\": %llu }", total); | ||
body_data.len = strlen((char*)body_data.ptr); | ||
if (!wasi_io_streams_method_output_stream_blocking_write_and_flush( | ||
wasi_io_streams_borrow_output_stream(out_body_stream), &body_data, &stream_err)) { | ||
set_string(ret, "Failed to write body"); | ||
wasi_io_streams_output_stream_drop_own(out_body_stream); | ||
wasi_http_types_outgoing_body_drop_own(out_body); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
log("Finished writing the outgoing request stream\n"); | ||
|
||
wasi_io_streams_output_stream_drop_own(out_body_stream); | ||
|
||
wasi_http_types_error_code_t err; | ||
if (!wasi_http_types_static_outgoing_body_finish(out_body, NULL, &err)) { | ||
set_string(ret, "Failed to finish body"); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
log("Finished setting up the request\n"); | ||
|
||
// Sending the request | ||
|
||
wasi_http_types_own_request_options_t request_options = wasi_http_types_constructor_request_options(); | ||
wasi_http_types_duration_t timeout = 5000000000; // 5 seconds (in ns) | ||
|
||
log("Setting the request options\n"); | ||
|
||
if (!wasi_http_types_method_request_options_set_connect_timeout( | ||
wasi_http_types_borrow_request_options(request_options), | ||
&timeout | ||
)) { | ||
log("Failed to set connect timeout\n") | ||
set_string(ret, "Failed to set connect timeout"); | ||
wasi_http_types_request_options_drop_own(request_options); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
if (!wasi_http_types_method_request_options_set_first_byte_timeout( | ||
wasi_http_types_borrow_request_options(request_options), | ||
&timeout | ||
)) { | ||
log("Failed to set first byte timeout\n") | ||
set_string(ret, "Failed to set first byte timeout"); | ||
wasi_http_types_request_options_drop_own(request_options); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
if (!wasi_http_types_method_request_options_set_between_bytes_timeout( | ||
wasi_http_types_borrow_request_options(request_options), | ||
&timeout | ||
)) { | ||
log("Failed to set between-bytes timeout\n") | ||
set_string(ret, "Failed to set between-bytes timeout"); | ||
wasi_http_types_request_options_drop_own(request_options); | ||
wasi_http_types_outgoing_request_drop_own(request); | ||
return; | ||
} | ||
|
||
log("Sending the request\n"); | ||
|
||
wasi_http_outgoing_handler_own_future_incoming_response_t future_response; | ||
wasi_http_outgoing_handler_error_code_t err_code; | ||
if (!wasi_http_outgoing_handler_handle(request, &request_options, &future_response, &err_code)) { | ||
set_string(ret, "Failed to send request"); | ||
return; | ||
} | ||
|
||
// Awaiting for the response | ||
|
||
bool got_response = false; | ||
wasi_http_types_own_incoming_response_t response; | ||
|
||
while (!got_response) { | ||
wasi_http_types_result_result_own_incoming_response_error_code_void_t res; | ||
if (wasi_http_types_method_future_incoming_response_get(wasi_http_types_borrow_future_incoming_response(future_response), &res)) { | ||
if (!res.is_err && !res.val.ok.is_err) { | ||
log("Got response\n"); | ||
response = res.val.ok.val.ok; | ||
got_response = true; | ||
} else if (!res.is_err && res.val.ok.is_err) { | ||
log("Returned with an error code: %u\n", res.val.ok.val.err.tag); | ||
set_string(ret, "Returned with error code"); | ||
wasi_http_types_future_incoming_response_drop_own(future_response); | ||
return; | ||
} else { | ||
log("Returned with an error\n"); | ||
set_string(ret, "Returned with error"); | ||
wasi_http_types_future_incoming_response_drop_own(future_response); | ||
return; | ||
} | ||
} else { | ||
log("No result yet, polling\n"); | ||
|
||
wasi_http_types_own_pollable_t pollable = wasi_http_types_method_future_incoming_response_subscribe( | ||
wasi_http_types_borrow_future_incoming_response(future_response) | ||
); | ||
wasi_io_poll_list_borrow_pollable_t pollable_list; | ||
pollable_list.len = 1; | ||
pollable_list.ptr = malloc(sizeof(wasi_io_poll_borrow_pollable_t)); | ||
pollable_list.ptr[0] = wasi_io_poll_borrow_pollable(pollable); | ||
|
||
c_example_http_list_u32_t poll_result; | ||
wasi_io_poll_poll(&pollable_list, &poll_result); | ||
wasi_io_poll_pollable_drop_own(pollable); | ||
} | ||
} | ||
|
||
// Processing the response | ||
|
||
wasi_http_types_status_code_t status = wasi_http_types_method_incoming_response_status( | ||
wasi_http_types_borrow_incoming_response(response) | ||
); | ||
|
||
log("Got response with status %u\n", status); | ||
|
||
wasi_http_types_own_incoming_body_t incoming_body; | ||
if (!wasi_http_types_method_incoming_response_consume(wasi_http_types_borrow_incoming_response(response), &incoming_body)) { | ||
set_string(ret, "Failed to consume response"); | ||
wasi_http_types_incoming_response_drop_own(response); | ||
return; | ||
} | ||
|
||
wasi_http_types_own_input_stream_t incoming_body_stream; | ||
if (!wasi_http_types_method_incoming_body_stream(wasi_http_types_borrow_incoming_body(incoming_body), &incoming_body_stream)) { | ||
set_string(ret, "Failed to get body stream"); | ||
wasi_http_types_incoming_body_drop_own(incoming_body); | ||
wasi_http_types_incoming_response_drop_own(response); | ||
return; | ||
} | ||
|
||
bool eof = false; | ||
uint8_t *full_body = malloc(0); | ||
uint64_t len = 0; | ||
|
||
while (!eof) { | ||
c_example_http_list_u8_t chunk; | ||
wasi_io_streams_stream_error_t stream_err; | ||
if (wasi_io_streams_method_input_stream_blocking_read(wasi_io_streams_borrow_input_stream(incoming_body_stream), 1024, &chunk, &stream_err)) { | ||
len += chunk.len; | ||
full_body = realloc(full_body, len); | ||
memcpy(full_body + len - chunk.len, chunk.ptr, chunk.len); | ||
} else { | ||
if (stream_err.tag == WASI_IO_STREAMS_STREAM_ERROR_CLOSED) { | ||
eof = true; | ||
} else { | ||
set_string(ret, "Failed to read from body stream"); | ||
wasi_io_streams_input_stream_drop_own(incoming_body_stream); | ||
wasi_http_types_incoming_body_drop_own(incoming_body); | ||
wasi_http_types_incoming_response_drop_own(response); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
wasi_io_streams_input_stream_drop_own(incoming_body_stream); | ||
wasi_http_types_incoming_body_drop_own(incoming_body); | ||
wasi_http_types_incoming_response_drop_own(response); | ||
|
||
log("Returning %llu characters\n", len) | ||
|
||
ret->ptr = full_body; | ||
ret->len = len; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"description": "A stateful Golem worker using WASI HTTP to send a request and process the response", | ||
"requiresAdapter": true, | ||
"requiresGolemHostWIT": true, | ||
"requiresWASI": true, | ||
"exclude": [ | ||
"component_name/component_name.c", | ||
"component_name/component_name.h", | ||
"component_name/component_name_component_type.o", | ||
"component_name.module.wasm", | ||
"component_name.wasm" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package pack:name; | ||
|
||
// See https://component-model.bytecodealliance.org/design/wit.html for more details about the WIT syntax | ||
|
||
interface api { | ||
add: func(value: u64); | ||
get: func() -> u64; | ||
send: func() -> string; | ||
} | ||
|
||
world component-name { | ||
import golem:api/host@0.2.0; | ||
import golem:rpc/types@0.1.0; | ||
|
||
import wasi:blobstore/blobstore; | ||
import wasi:blobstore/container; | ||
import wasi:cli/environment@0.2.0; | ||
import wasi:clocks/wall-clock@0.2.0; | ||
import wasi:clocks/monotonic-clock@0.2.0; | ||
import wasi:filesystem/preopens@0.2.0; | ||
import wasi:filesystem/types@0.2.0; | ||
import wasi:http/types@0.2.0; | ||
import wasi:http/outgoing-handler@0.2.0; | ||
import wasi:io/error@0.2.0; | ||
import wasi:io/poll@0.2.0; | ||
import wasi:io/streams@0.2.0; | ||
import wasi:keyvalue/eventual-batch@0.1.0; | ||
import wasi:keyvalue/eventual@0.1.0; | ||
import wasi:logging/logging; | ||
import wasi:random/random@0.2.0; | ||
import wasi:random/insecure@0.2.0; | ||
import wasi:random/insecure-seed@0.2.0; | ||
import wasi:sockets/ip-name-lookup@0.2.0; | ||
import wasi:sockets/instance-network@0.2.0; | ||
|
||
export api; | ||
} |