OSDN Git Service

gobex: Add support for on-demand headers
authorJohan Hedberg <johan.hedberg@intel.com>
Thu, 30 Jun 2011 19:57:51 +0000 (22:57 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 4 Dec 2012 21:21:58 +0000 (22:21 +0100)
gobex/gobex-header.c
gobex/gobex-header.h
gobex/gobex-packet.c
unit/test-gobex-header.c
unit/test-gobex-packet.c
unit/test-gobex.c

index ff24589..0ef5e24 100644 (file)
@@ -43,6 +43,9 @@ struct _GObexHeader {
                guint8 u8;
                guint32 u32;
        } v;
+
+       GObexHeaderDataFunc get_data;
+       gpointer get_data_data;
 };
 
 static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
@@ -80,6 +83,20 @@ static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
        return (from + count);
 }
 
+static gsize get_data(GObexHeader *header, guint8 *buf, gsize len)
+{
+       guint16 u16;
+
+       header->vlen = header->get_data(header, buf + 2, len - 2,
+                                               header->get_data_data);
+
+       header->hlen = header->vlen + 3;
+       u16 = g_htons(header->hlen);
+       memcpy(buf, &u16, sizeof(u16));
+
+       return header->hlen;
+}
+
 gsize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
 {
        guint8 *ptr = buf;
@@ -105,6 +122,9 @@ gsize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
                g_free(utf16);
                break;
        case G_OBEX_HDR_TYPE_BYTES:
+               if (header->get_data)
+                       return get_data(header, ptr, buf_len - 1);
+
                u16 = g_htons(header->hlen);
                ptr = put_bytes(ptr, &u16, sizeof(u16));
                if (header->extdata)
@@ -362,6 +382,24 @@ GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, gsize len,
        return header;
 }
 
+GObexHeader *g_obex_header_new_on_demand(guint8 id, GObexHeaderDataFunc func,
+                                                       gpointer user_data)
+{
+       GObexHeader *header;
+
+       if (G_OBEX_HDR_TYPE(id) != G_OBEX_HDR_TYPE_BYTES)
+               return NULL;
+
+       header = g_new0(GObexHeader, 1);
+
+       header->id = id;
+       header->hlen = 3;
+       header->get_data = func;
+       header->get_data_data = user_data;
+
+       return header;
+}
+
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
 {
        GObexHeader *header;
index 1df7f14..e7f16e5 100644 (file)
@@ -55,6 +55,9 @@
 
 typedef struct _GObexHeader GObexHeader;
 
+typedef guint16 (*GObexHeaderDataFunc) (GObexHeader *header, void *buf,
+                                               gsize len, gpointer user_data);
+
 gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str);
 gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
                                                                gsize *len);
@@ -64,6 +67,9 @@ gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val);
 GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str);
 GObexHeader *g_obex_header_new_bytes(guint8 id, void *data, gsize len,
                                                GObexDataPolicy data_policy);
+GObexHeader *g_obex_header_new_on_demand(guint8 id,
+                                               GObexHeaderDataFunc func,
+                                               gpointer user_data);
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val);
 GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val);
 
index a74b129..89f14e4 100644 (file)
@@ -237,21 +237,16 @@ failed:
 gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len)
 {
        gsize count;
-       guint16 pkt_len, u16;
+       guint16 u16;
        GSList *l;
 
-       pkt_len = 3 + pkt->data_len + pkt->hlen;
-
-       if (pkt_len > len)
+       if (3 + pkt->data_len + pkt->hlen > len)
                return -ENOBUFS;
 
        buf[0] = pkt->opcode;
        if (pkt->final)
                buf[0] |= FINAL_BIT;
 
-       u16 = g_htons(pkt_len);
-       memcpy(&buf[1], &u16, sizeof(u16));
-
        if (pkt->data_len > 0) {
                if (pkt->data_policy == G_OBEX_DATA_REF)
                        memcpy(&buf[3], pkt->data.buf_ref, pkt->data_len);
@@ -263,10 +258,13 @@ gssize g_obex_packet_encode(GObexPacket *pkt, guint8 *buf, gsize len)
 
        for (l = pkt->headers; l != NULL; l = g_slist_next(l)) {
                GObexHeader *hdr = l->data;
+               if (count >= len)
+                       return -ENOBUFS;
                count += g_obex_header_encode(hdr, buf + count, len - count);
        }
 
-       g_assert_cmpuint(count, ==, pkt_len);
+       u16 = g_htons(count);
+       memcpy(&buf[1], &u16, sizeof(u16));
 
        return count;
 }
index d2dd99a..61536e2 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <gobex/gobex-header.h>
 
@@ -129,6 +130,32 @@ static void test_header_uint32(void)
        g_obex_header_free(header);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+                                                       gpointer user_data)
+{
+       uint8_t body_data[] = { 1, 2, 3, 4 };
+
+       memcpy(buf, body_data, sizeof(body_data));
+
+       return sizeof(body_data);
+}
+
+static void test_header_on_demand(void)
+{
+       GObexHeader *header;
+       uint8_t buf[1024];
+       size_t len;
+
+       header = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+                                               get_body_data, NULL);
+
+       len = g_obex_header_encode(header, buf, sizeof(buf));
+
+       assert_memequal(hdr_body, sizeof(hdr_body), buf, len);
+
+       g_obex_header_free(header);
+}
+
 static GObexHeader *parse_and_encode(uint8_t *buf, size_t buf_len)
 {
        GObexHeader *header;
@@ -457,6 +484,8 @@ int main(int argc, char *argv[])
        g_test_add_func("/gobex/test_header_uint8", test_header_uint8);
        g_test_add_func("/gobex/test_header_uint32", test_header_uint32);
 
+       g_test_add_func("/gobex/test_header_on_demand", test_header_on_demand);
+
        g_test_run();
 
        return 0;
index afc8adf..464382a 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 
 #include <gobex/gobex-packet.h>
 
@@ -31,6 +32,9 @@ static uint8_t pkt_connect[] = { G_OBEX_OP_CONNECT, 0x00, 0x0c,
                                                0x00, 0x05, 0xab, 0xcd };
 static uint8_t pkt_put_action[] = { G_OBEX_OP_PUT, 0x00, 0x05,
                                        G_OBEX_HDR_ID_ACTION, 0xab };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+                                       G_OBEX_HDR_ID_BODY, 0x00, 0x07,
+                                       1, 2, 3, 4 };
 static uint8_t pkt_put[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
 
 static uint8_t pkt_nval_len[] = { G_OBEX_OP_PUT, 0xab, 0xcd, 0x12 };
@@ -140,6 +144,40 @@ static void test_decode_encode(void)
        g_obex_packet_free(pkt);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+                                                       gpointer user_data)
+{
+       uint8_t data[] = { 1, 2, 3, 4 };
+
+       memcpy(buf, data, sizeof(data));
+
+       return sizeof(data);
+}
+
+static void test_encode_on_demand(void)
+{
+       GObexPacket *pkt;
+       GObexHeader *hdr;
+       uint8_t buf[255];
+       gssize len;
+
+       pkt = g_obex_packet_new(G_OBEX_OP_PUT, FALSE);
+
+       hdr = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+                                               get_body_data, NULL);
+       g_obex_packet_add_header(pkt, hdr);
+
+       len = g_obex_packet_encode(pkt, buf, sizeof(buf));
+       if (len < 0) {
+               g_printerr("Encoding failed: %s\n", g_strerror(-len));
+               g_assert_not_reached();
+       }
+
+       assert_memequal(pkt_put_body, sizeof(pkt_put_body), buf, len);
+
+       g_obex_packet_free(pkt);
+}
+
 int main(int argc, char *argv[])
 {
        g_test_init(&argc, &argv, NULL);
@@ -155,6 +193,8 @@ int main(int argc, char *argv[])
 
        g_test_add_func("/gobex/test_encode_pkt", test_decode_encode);
 
+       g_test_add_func("/gobex/test_encode_on_demand", test_encode_on_demand);
+
        g_test_run();
 
        return 0;
index 13a0668..2d8630e 100644 (file)
@@ -43,6 +43,9 @@ static uint8_t pkt_nval_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x05,
                                        0x10, 0x00, };
 static uint8_t pkt_abort_rsp[] = { 0x90, 0x00, 0x03 };
 static uint8_t pkt_nval_short_rsp[] = { 0x10 | FINAL_BIT, 0x12 };
+static uint8_t pkt_put_body[] = { G_OBEX_OP_PUT, 0x00, 0x0a,
+                                       G_OBEX_HDR_ID_BODY, 0x00, 0x07,
+                                       1, 2, 3, 4 };
 
 static gboolean test_timeout(gpointer user_data)
 {
@@ -58,49 +61,6 @@ static gboolean test_timeout(gpointer user_data)
        return FALSE;
 }
 
-static gboolean handle_connect_data(GIOChannel *io, GIOCondition cond,
-                                                       gpointer user_data)
-{
-       GError **err = user_data;
-       GIOStatus status;
-       gsize rbytes;
-       char buf[255];
-
-       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
-               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-                               "Unexpected condition %d on socket", cond);
-               goto done;
-       }
-
-       status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
-       if (status != G_IO_STATUS_NORMAL) {
-               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-                               "Reading data failed with status %d", status);
-               goto done;
-       }
-
-       if (rbytes != sizeof(pkt_connect_req)) {
-               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-                               "Got %zu bytes instead of %zu",
-                               rbytes, sizeof(pkt_connect_req));
-               dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
-                                                               buf, rbytes);
-               goto done;
-       }
-
-       if (memcmp(buf, pkt_connect_req, rbytes) != 0) {
-               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
-                               "Mismatch with received data");
-               dump_bufs(pkt_connect_req, sizeof(pkt_connect_req),
-                                                               buf, rbytes);
-               goto done;
-       }
-
-done:
-       g_main_loop_quit(mainloop);
-       return FALSE;
-}
-
 static GObex *create_gobex(int fd, GObexTransportType transport_type,
                                                gboolean close_on_unref)
 {
@@ -272,25 +232,18 @@ static gboolean send_nothing(GIOChannel *io, GIOCondition cond,
        return FALSE;
 }
 
-static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
-                                       gint req_timeout, int transport_type)
+static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
+                               GIOFunc send_rsp_func, gint req_timeout,
+                               int transport_type)
 {
-       guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
        GError *gerr = NULL;
        GIOChannel *io;
        GIOCondition cond;
-       GObexPacket *req;
        guint io_id, timer_id, test_time;
        GObex *obex;
 
        create_endpoints(&obex, &io, transport_type);
 
-       req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
-       g_assert(req != NULL);
-
-       g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
-                                                       G_OBEX_DATA_REF);
-
        g_obex_send_req(obex, req, req_timeout, rsp_func, &gerr, &gerr);
        g_assert_no_error(gerr);
 
@@ -319,6 +272,21 @@ static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
        g_assert_no_error(gerr);
 }
 
+static void send_connect(GObexResponseFunc rsp_func, GIOFunc send_rsp_func,
+                                       gint req_timeout, int transport_type)
+{
+       GObexPacket *req;
+       guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+
+       req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
+       g_assert(req != NULL);
+
+       g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
+                                                       G_OBEX_DATA_REF);
+
+       send_req(req, rsp_func, send_rsp_func, req_timeout, transport_type);
+}
+
 static void test_send_connect_req_stream(void)
 {
        send_connect(connect_rsp, send_connect_rsp, -1, SOCK_STREAM);
@@ -498,10 +466,56 @@ static void test_cancel_req_delay_pkt(void)
        test_cancel_req_delay(SOCK_SEQPACKET);
 }
 
+struct rcv_buf_info {
+       GError *err;
+       const guint8 *buf;
+       gsize len;
+};
+
+static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+       struct rcv_buf_info *r = user_data;
+       GIOStatus status;
+       gsize rbytes;
+       char buf[255];
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Unexpected condition %d on socket", cond);
+               goto done;
+       }
+
+       status = g_io_channel_read_chars(io, buf, sizeof(buf), &rbytes, NULL);
+       if (status != G_IO_STATUS_NORMAL) {
+               g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Reading data failed with status %d", status);
+               goto done;
+       }
+
+       if (rbytes != r->len) {
+               g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Got %zu bytes instead of %zu",
+                               rbytes, sizeof(pkt_connect_req));
+               dump_bufs(r->buf, r->len, buf, rbytes);
+               goto done;
+       }
+
+       if (memcmp(buf, r->buf, rbytes) != 0) {
+               g_set_error(&r->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Mismatch with received data");
+               dump_bufs(r->buf, r->len, buf, rbytes);
+               goto done;
+       }
+
+done:
+       g_main_loop_quit(mainloop);
+       return FALSE;
+}
+
 static void test_send_connect(int transport_type)
 {
        guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
-       GError *gerr = NULL;
+       struct rcv_buf_info r;
        GIOChannel *io;
        GIOCondition cond;
        GObexPacket *req;
@@ -510,20 +524,24 @@ static void test_send_connect(int transport_type)
 
        create_endpoints(&obex, &io, transport_type);
 
+       r.err = NULL;
+       r.buf = pkt_connect_req;
+       r.len = sizeof(pkt_connect_req);
+
        req = g_obex_packet_new(G_OBEX_OP_CONNECT, TRUE);
        g_assert(req != NULL);
 
        g_obex_packet_set_data(req, connect_data, sizeof(connect_data),
                                                        G_OBEX_DATA_REF);
-       g_obex_send(obex, req, &gerr);
-       g_assert_no_error(gerr);
+       g_obex_send(obex, req, &r.err);
+       g_assert_no_error(r.err);
 
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-       io_id = g_io_add_watch(io, cond, handle_connect_data, &gerr);
+       io_id = g_io_add_watch(io, cond, rcv_data, &r);
 
        mainloop = g_main_loop_new(NULL, FALSE);
 
-       timer_id = g_timeout_add_seconds(1, test_timeout, &gerr);
+       timer_id = g_timeout_add_seconds(1, test_timeout, &r.err);
 
        g_main_loop_run(mainloop);
 
@@ -535,7 +553,7 @@ static void test_send_connect(int transport_type)
        g_source_remove(io_id);
        g_obex_unref(obex);
 
-       g_assert_no_error(gerr);
+       g_assert_no_error(r.err);
 }
 
 static void test_send_connect_stream(void)
@@ -548,6 +566,71 @@ static void test_send_connect_pkt(void)
        test_send_connect(SOCK_SEQPACKET);
 }
 
+static guint16 get_body_data(GObexHeader *header, void *buf, gsize len,
+                                                       gpointer user_data)
+{
+       uint8_t data[] = { 1, 2, 3, 4 };
+
+       memcpy(buf, data, sizeof(data));
+
+       return sizeof(data);
+}
+
+static void test_send_on_demand(int transport_type)
+{
+       struct rcv_buf_info r;
+       GIOChannel *io;
+       GIOCondition cond;
+       GObexPacket *req;
+       GObexHeader *hdr;
+       guint io_id, timer_id;
+       GObex *obex;
+
+       create_endpoints(&obex, &io, transport_type);
+
+       r.err = NULL;
+       r.buf = pkt_put_body;
+       r.len = sizeof(pkt_put_body);
+
+       req = g_obex_packet_new(G_OBEX_OP_PUT, FALSE);
+
+       hdr = g_obex_header_new_on_demand(G_OBEX_HDR_ID_BODY,
+                                               get_body_data, NULL);
+       g_obex_packet_add_header(req, hdr);
+
+       g_obex_send(obex, req, &r.err);
+       g_assert_no_error(r.err);
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, rcv_data, &r);
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &r.err);
+
+       g_main_loop_run(mainloop);
+
+       g_main_loop_unref(mainloop);
+       mainloop = NULL;
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(r.err);
+}
+
+static void test_send_on_demand_stream(void)
+{
+       test_send_on_demand(SOCK_STREAM);
+}
+
+static void test_send_on_demand_pkt(void)
+{
+       test_send_on_demand(SOCK_SEQPACKET);
+}
+
 static void handle_connect_event(GObex *obex, GError *err, GObexPacket *pkt,
                                                        gpointer user_data)
 {
@@ -703,6 +786,10 @@ int main(int argc, char *argv[])
                                                test_send_connect_stream);
        g_test_add_func("/gobex/test_send_connect_pkt",
                                                test_send_connect_pkt);
+       g_test_add_func("/gobex/test_send_on_demand_stream",
+                                               test_send_on_demand_stream);
+       g_test_add_func("/gobex/test_send_on_demand_pkt",
+                                               test_send_on_demand_pkt);
        g_test_add_func("/gobex/test_send_connect_req_stream",
                                        test_send_connect_req_stream);
        g_test_add_func("/gobex/test_send_connect_req_pkt",
@@ -718,6 +805,7 @@ int main(int argc, char *argv[])
        g_test_add_func("/gobex/test_send_connect_req_timeout_pkt",
                                        test_send_connect_req_timeout_pkt);
 
+
        g_test_add_func("/gobex/test_cancel_req_immediate",
                                        test_cancel_req_immediate);
        g_test_add_func("/gobex/test_cancel_req_delay_stream",