1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "echo.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include <zircon/syscalls.h>
12#include <unittest/unittest.h>
13
14#include "message.h"
15#include "struct.h"
16
17bool wait_for_readable(zx_handle_t handle) {
18    unittest_printf("waiting for handle %u to be readable (or closed)\n", handle);
19    // Wait for |handle| to become readable or closed.
20    zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
21    zx_signals_t pending;
22    zx_status_t wait_status = zx_object_wait_one(handle, signals, ZX_TIME_INFINITE,
23                                                       &pending);
24    if (wait_status != ZX_OK) {
25        return false;
26    }
27    if (!(pending & ZX_CHANNEL_READABLE)) {
28        return false;
29    }
30    return true;
31}
32
33bool serve_echo_request(zx_handle_t handle) {
34    ASSERT_TRUE(wait_for_readable(handle), "handle not readable");
35
36    // Try to read a message from |in_handle|.
37    // First, figure out size.
38    uint32_t in_msg_size = 0u;
39    zx_status_t read_status = zx_channel_read(handle, 0u, NULL, NULL, 0, 0, &in_msg_size, NULL);
40    ASSERT_NE(read_status, ZX_ERR_NO_MEMORY, "unexpected sizing read status");
41
42    unittest_printf("reading message of size %u\n", in_msg_size);
43    void* in_msg_buf = calloc(in_msg_size, 1u);
44    read_status = zx_channel_read(handle, 0u, in_msg_buf, NULL, in_msg_size, 0, &in_msg_size, NULL);
45    ASSERT_EQ(read_status, ZX_OK, "read failed with status");
46
47    // Try to parse message data.
48    ASSERT_TRUE(mojo_validate_struct_header(in_msg_buf, in_msg_size),
49                "validation failed on read message");
50
51    mojo_struct_header_t* in_struct_header = (mojo_struct_header_t*)in_msg_buf;
52    ASSERT_EQ(in_struct_header->version, 1u, "Header verison incorrect");
53
54    mojo_message_header_with_request_id_t* in_msg_header =
55        (mojo_message_header_with_request_id_t*)in_struct_header;
56
57    ASSERT_EQ(in_msg_header->message_header.name, 0u, "Name should be null");
58
59    ASSERT_EQ(in_msg_header->message_header.flags,
60              (uint32_t)MOJO_MESSAGE_HEADER_FLAGS_EXPECTS_RESPONSE,
61              "Invalid header flag");
62
63    uint64_t request_id = in_msg_header->request_id;
64
65    void* in_payload = in_msg_header + 1u;
66
67    uint32_t in_string_header_num_bytes = *(uint32_t*)in_payload;
68    uint32_t in_string_header_num_elems = *((uint32_t*)in_payload + 1u);
69    void* in_string_data = ((uint32_t*)in_payload) + 2u;
70    unittest_printf("got string: ");
71    for (uint32_t i = 0u; i < in_string_header_num_elems; ++i) {
72        unittest_printf("%c", ((char*)in_string_data)[i]);
73    }
74    unittest_printf("\n");
75
76    // TODO: Validate array header
77
78    // Incoming message seems fine, form an outgoing message and send it.
79
80    void* out_msg_buf = malloc(in_msg_size);
81    uint32_t out_msg_size = in_msg_size;
82
83    // Write header
84    mojo_message_header_with_request_id_t* out_msg_header =
85        (mojo_message_header_with_request_id_t*)out_msg_buf;
86
87    // Struct header
88    out_msg_header->message_header.struct_header.num_bytes =
89        sizeof(mojo_message_header_with_request_id_t);
90    out_msg_header->message_header.struct_header.version = 1u;
91
92    // Message header
93    out_msg_header->message_header.name = 0u;
94    out_msg_header->message_header.flags = MOJO_MESSAGE_HEADER_FLAGS_IS_RESPONSE;
95    out_msg_header->request_id = request_id;
96
97    uint32_t* out_string_header = (uint32_t*)out_msg_header + 1u;
98    *out_string_header = in_string_header_num_bytes;
99    *(out_string_header + 1u) = in_string_header_num_elems;
100
101    if (in_string_header_num_bytes != 0u) {
102        char* out_string_dest = (char*)(out_string_header + 2u);
103        memcpy(out_string_dest, (char*)(in_string_data), in_string_header_num_bytes);
104    }
105    free(in_msg_buf);
106
107    zx_status_t write_status =
108        zx_channel_write(handle, 0u, out_msg_buf, out_msg_size, NULL, 0u);
109    free(out_msg_buf);
110
111    ASSERT_EQ(write_status, ZX_OK, "Error while message writing");
112
113    unittest_printf("served request!\n\n");
114    return true;
115}
116
117bool echo_test(void) {
118    BEGIN_TEST;
119    zx_handle_t handles[2] = {0};
120    zx_status_t status = zx_channel_create(0, handles, handles + 1);
121    ASSERT_EQ(status, 0, "could not create channel");
122    unittest_printf("created channel with handle values %u and %u\n", handles[0], handles[1]);
123    for (int i = 0; i < 3; i++) {
124        unittest_printf("loop %d\n", i);
125        static const uint32_t buf[9] = {
126            24,         // struct header, num_bytes
127            1,          // struct header: version
128            0,          // struct header: flags
129            1,          // message header: name
130            0, 0,       // message header: request id (8 bytes)
131            4,          // array header: num bytes
132            4,          // array header: num elems
133            0x42424143, // array contents: 'CABB'
134        };
135        zx_status_t status = zx_channel_write(handles[1], 0u, (void*)buf, sizeof(buf), NULL, 0u);
136        ASSERT_EQ(status, ZX_OK, "could not write echo request");
137
138        ASSERT_TRUE(serve_echo_request(handles[0]), "serve_echo_request failed");
139    }
140    zx_handle_close(handles[1]);
141    EXPECT_FALSE(wait_for_readable(handles[0]), "handle should not readable");
142    zx_handle_close(handles[0]);
143    END_TEST;
144}
145
146BEGIN_TEST_CASE(echo_tests)
147RUN_TEST(echo_test)
148END_TEST_CASE(echo_tests)
149
150
151#ifndef BUILD_COMBINED_TESTS
152int main(int argc, char** argv) {
153    return unittest_run_all_tests(argc, argv) ? 0 : -1;
154}
155#endif
156