OSDN Git Service

gobex: Add basic response handling
authorJohan Hedberg <johan.hedberg@intel.com>
Mon, 27 Jun 2011 13:17:04 +0000 (16:17 +0300)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 4 Dec 2012 21:21:56 +0000 (22:21 +0100)
gobex/gobex.c
gobex/gobex.h
unit/test-gobex.c

index 485b91d..3e06d86 100644 (file)
@@ -67,6 +67,10 @@ struct _GObexPacket {
 
        size_t hlen;            /* Length of all encoded headers */
        GSList *headers;
+
+       guint id;
+       GObexResponseFunc rsp_func;
+       gpointer rsp_data;
 };
 
 struct _GObex {
@@ -91,6 +95,15 @@ struct _GObex {
 
        GObexRequestFunc req_func;
        gpointer req_func_data;
+
+       struct pending_req *pending_req;
+};
+
+struct pending_req {
+       guint id;
+       guint8 opcode;
+       GObexResponseFunc rsp_func;
+       gpointer rsp_data;
 };
 
 struct connect_data {
@@ -378,6 +391,19 @@ GObexHeader *g_obex_header_uint32(guint8 id, guint32 val)
        return header;
 }
 
+guint g_obex_packet_set_response_function(GObexPacket *pkt,
+                                                       GObexResponseFunc func,
+                                                       gpointer user_data)
+{
+       static guint next_id = 1;
+
+       pkt->rsp_func = func;
+       pkt->rsp_data = user_data;
+       pkt->id = next_id++;
+
+       return pkt->id;
+}
+
 guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final)
 {
        if (final)
@@ -448,7 +474,7 @@ void g_obex_packet_free(GObexPacket *pkt)
        g_free(pkt);
 }
 
-static ssize_t get_header_offset(guint8 opcode)
+static ssize_t req_header_offset(guint8 opcode)
 {
        switch (opcode) {
        case G_OBEX_OP_CONNECT:
@@ -466,6 +492,23 @@ static ssize_t get_header_offset(guint8 opcode)
        }
 }
 
+static ssize_t rsp_header_offset(guint8 opcode)
+{
+       switch (opcode) {
+       case G_OBEX_OP_CONNECT:
+               return sizeof(struct connect_data);
+       case G_OBEX_OP_SETPATH:
+       case G_OBEX_OP_DISCONNECT:
+       case G_OBEX_OP_PUT:
+       case G_OBEX_OP_GET:
+       case G_OBEX_OP_SESSION:
+       case G_OBEX_OP_ABORT:
+               return 0;
+       default:
+               return -1;
+       }
+}
+
 static gboolean parse_headers(GObexPacket *pkt, const void *data, size_t len,
                                                GObexDataPolicy data_policy)
 {
@@ -489,12 +532,12 @@ static gboolean parse_headers(GObexPacket *pkt, const void *data, size_t len,
 }
 
 GObexPacket *g_obex_packet_decode(const void *data, size_t len,
+                                               size_t header_offset,
                                                GObexDataPolicy data_policy)
 {
        const guint8 *buf = data;
        guint16 packet_len;
        guint8 opcode;
-       ssize_t header_offset;
        GObexPacket *pkt;
        gboolean final;
 
@@ -511,16 +554,12 @@ GObexPacket *g_obex_packet_decode(const void *data, size_t len,
        final = (opcode & G_OBEX_FINAL) ? TRUE : FALSE;
        opcode &= ~G_OBEX_FINAL;
 
-       header_offset = get_header_offset(opcode);
-       if (header_offset < 0)
-               return NULL;
-
        pkt = g_obex_packet_new(opcode, final);
 
        if (header_offset == 0)
                goto headers;
 
-       if (3 + header_offset < (ssize_t) len)
+       if (3 + header_offset < len)
                goto failed;
 
        if (data_policy == G_OBEX_DATA_INHERIT)
@@ -580,6 +619,25 @@ static ssize_t g_obex_packet_encode(GObexPacket *pkt, uint8_t *buf, size_t len)
        return count;
 }
 
+static void pending_req_free(struct pending_req *req)
+{
+       g_free(req);
+}
+
+static struct pending_req *pending_req_new(GObexPacket *pkt)
+{
+       struct pending_req *req;
+
+       req = g_new0(struct pending_req, 1);
+
+       req->id = pkt->id;
+       req->rsp_func = pkt->rsp_func;
+       req->rsp_data = pkt->rsp_data;
+       req->opcode = pkt->opcode;
+
+       return req;
+}
+
 static gboolean write_data(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
@@ -601,12 +659,20 @@ static gboolean write_data(GIOChannel *io, GIOCondition cond,
                if (pkt == NULL)
                        goto done;
 
+               /* Can't send a request while there's a pending one */
+               if (obex->pending_req && pkt->id > 0)
+                       goto done;
+
                len = g_obex_packet_encode(pkt, obex->tx_buf, obex->tx_mtu);
+               if (len < 0) {
+                       g_obex_packet_free(pkt);
+                       goto done;
+               }
 
-               g_obex_packet_free(pkt);
+               if (pkt->id > 0)
+                       obex->pending_req = pending_req_new(pkt);
 
-               if (len < 0)
-                       goto done;
+               g_obex_packet_free(pkt);
 
                obex->tx_data = len;
                obex->tx_sent = 0;
@@ -648,6 +714,24 @@ gboolean g_obex_send(GObex *obex, GObexPacket *pkt)
        return TRUE;
 }
 
+guint g_obex_send_req(GObex *obex, GObexPacket *req, GObexResponseFunc func,
+                                                       gpointer user_data)
+{
+       guint id;
+
+       id = g_obex_packet_set_response_function(req, func, user_data);
+
+       if (!g_obex_send(obex, req))
+               return 0;
+
+       return id;
+}
+
+gboolean g_obex_cancel_req(GObex *obex, guint req_id)
+{
+       return TRUE;
+}
+
 void g_obex_set_request_function(GObex *obex, GObexRequestFunc func,
                                                        gpointer user_data)
 {
@@ -655,10 +739,29 @@ void g_obex_set_request_function(GObex *obex, GObexRequestFunc func,
        obex->req_func_data = user_data;
 }
 
-static gboolean g_obex_handle_packet(GObex *obex, GObexPacket *pkt)
+static void handle_response(GObex *obex, GObexPacket *rsp)
+{
+       struct pending_req *req = obex->pending_req;
+
+       if (req->rsp_func)
+               req->rsp_func(obex, NULL, rsp, req->rsp_data);
+
+       pending_req_free(req);
+       obex->pending_req = NULL;
+}
+
+static void handle_request(GObex *obex, GObexPacket *req)
 {
        if (obex->req_func)
-               obex->req_func(obex, pkt, obex->req_func_data);
+               obex->req_func(obex, req, obex->req_func_data);
+}
+
+static gboolean g_obex_handle_packet(GObex *obex, GObexPacket *pkt)
+{
+       if (obex->pending_req)
+               handle_response(obex, pkt);
+       else
+               handle_request(obex, pkt);
 
        return TRUE;
 }
@@ -712,6 +815,7 @@ static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
 {
        GObex *obex = user_data;
        GObexPacket *pkt;
+       ssize_t header_offset;
 
        if (cond & G_IO_NVAL)
                return FALSE;
@@ -724,7 +828,17 @@ static gboolean incoming_data(GIOChannel *io, GIOCondition cond,
        if (obex->rx_data < 3 || obex->rx_data < obex->rx_pkt_len)
                return TRUE;
 
-       pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data,
+       if (obex->pending_req)
+               header_offset = rsp_header_offset(obex->pending_req->opcode);
+       else {
+               guint8 opcode = obex->rx_buf[0] & ~G_OBEX_FINAL;
+               header_offset = req_header_offset(opcode);
+       }
+
+       if (header_offset < 0)
+               goto failed;
+
+       pkt = g_obex_packet_decode(obex->rx_buf, obex->rx_data, header_offset,
                                                        G_OBEX_DATA_REF);
        if (pkt == NULL) {
                /* FIXME: Handle decoding error */
@@ -800,5 +914,8 @@ void g_obex_unref(GObex *obex)
        g_free(obex->rx_buf);
        g_free(obex->tx_buf);
 
+       if (obex->pending_req)
+               pending_req_free(obex->pending_req);
+
        g_free(obex);
 }
index 5e3612f..cc56cdf 100644 (file)
@@ -73,6 +73,8 @@ typedef struct _GObexHeader GObexHeader;
 
 typedef void (*GObexRequestFunc) (GObex *obex, GObexPacket *req,
                                                        gpointer user_data);
+typedef void (*GObexResponseFunc) (GObex *obex, GError *err, GObexPacket *rsp,
+                                                       gpointer user_data);
 
 GObexHeader *g_obex_header_unicode(guint8 id, const char *str);
 GObexHeader *g_obex_header_bytes(guint8 id, void *data, size_t len,
@@ -85,6 +87,9 @@ GObexHeader *g_obex_header_decode(const void *data, size_t len,
                                GObexDataPolicy data_policy, size_t *parsed);
 void g_obex_header_free(GObexHeader *header);
 
+guint g_obex_packet_set_response_function(GObexPacket *pkt,
+                                                       GObexResponseFunc func,
+                                                       gpointer user_data);
 guint8 g_obex_packet_get_operation(GObexPacket *pkt, gboolean *final);
 gboolean g_obex_packet_add_header(GObexPacket *pkt, GObexHeader *header);
 gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, size_t len,
@@ -93,10 +98,15 @@ GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final);
 void g_obex_packet_free(GObexPacket *pkt);
 
 GObexPacket *g_obex_packet_decode(const void *data, size_t len,
+                                               size_t header_offset,
                                                GObexDataPolicy data_policy);
 
 gboolean g_obex_send(GObex *obex, GObexPacket *pkt);
 
+guint g_obex_send_req(GObex *obex, GObexPacket *req, GObexResponseFunc func,
+                                                       gpointer user_data);
+gboolean g_obex_cancel_req(GObex *obex, guint req_id);
+
 void g_obex_set_request_function(GObex *obex, GObexRequestFunc func,
                                                        gpointer user_data);
 
index ff46b8e..54257d5 100644 (file)
@@ -34,6 +34,8 @@ static GMainLoop *mainloop = NULL;
 
 static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT,
                                        0x00, 0x07, 0x10, 0x00, 0x10, 0x00 };
+static uint8_t pkt_connect_rsp[] = { 0x10 | FINAL_BIT, 0x00, 0x07,
+                                       0x10, 0x00, 0x10, 0x00 };
 
 static uint8_t hdr_connid[] = { G_OBEX_HDR_ID_CONNECTION, 1, 2, 3, 4 };
 static uint8_t hdr_name_ascii[] = { G_OBEX_HDR_ID_NAME, 0x00, 0x0b,
@@ -173,6 +175,107 @@ static void create_endpoints(GObex **obex, GIOChannel **io, int sock_type)
        g_io_channel_set_close_on_unref(*io, TRUE);
 }
 
+static void connect_rsp(GObex *obex, GError *err, GObexPacket *rsp,
+                                                       gpointer user_data)
+{
+       guint8 rsp_code;
+       gboolean final;
+       GError **test_err = user_data;
+
+       if (err != NULL) {
+               g_assert(*test_err == NULL);
+               *test_err = g_error_copy(err);
+               goto done;
+       }
+
+       rsp_code = g_obex_packet_get_operation(rsp, &final);
+       if (rsp_code != 0x10) {
+               g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Unexpected response 0x%02x", rsp_code);
+               goto done;
+       }
+
+       if (!final) {
+               g_set_error(test_err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "Connect response didn't have final bit");
+               goto done;
+       }
+
+done:
+       g_main_loop_quit(mainloop);
+}
+
+static gboolean send_connect_rsp(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       GError **err = user_data;
+       gsize bytes_written, rbytes;
+       char buf[255];
+       GIOStatus status;
+
+       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,
+                                       "read failed with status %d", status);
+               goto failed;
+       }
+
+       g_io_channel_write_chars(io, (gchar *) pkt_connect_rsp,
+                                       sizeof(pkt_connect_rsp),
+                                       &bytes_written, NULL);
+       if (bytes_written != sizeof(pkt_connect_rsp)) {
+               g_set_error(err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                                               "Unable to write to socket");
+               goto failed;
+       }
+
+       return FALSE;
+
+failed:
+       g_main_loop_quit(mainloop);
+       return FALSE;
+}
+
+static void test_send_req_stream(void)
+{
+       guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
+       GError *gerr = NULL;
+       GIOChannel *io;
+       GIOCondition cond;
+       GObexPacket *req;
+       guint io_id, timer_id;
+       GObex *obex;
+
+       create_endpoints(&obex, &io, SOCK_STREAM);
+
+       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, connect_rsp, &gerr);
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, send_connect_rsp, &gerr);
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &gerr);
+
+       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(gerr);
+}
+
 static void test_send_connect_stream(void)
 {
        guint8 connect_data[] = { 0x10, 0x00, 0x10, 0x00 };
@@ -353,7 +456,7 @@ static void test_decode_pkt(void)
        GObexPacket *pkt;
        uint8_t buf[] = { G_OBEX_OP_PUT, 0x00, 0x03 };
 
-       pkt = g_obex_packet_decode(buf, sizeof(buf), G_OBEX_DATA_REF);
+       pkt = g_obex_packet_decode(buf, sizeof(buf), 0, G_OBEX_DATA_REF);
        g_assert(pkt != NULL);
 
        g_obex_packet_free(pkt);
@@ -627,6 +730,8 @@ int main(int argc, char *argv[])
                                                test_recv_connect_stream);
        g_test_add_func("/gobex/test_send_connect_stream",
                                                test_send_connect_stream);
+       g_test_add_func("/gobex/test_send_req_stream",
+                                               test_send_req_stream);
 
        g_test_run();