static int8_t on_post_wifi_request(uint8_t cid, GSwifi::GSREQUESTSTATE state) {
    if (state == GSwifi::GSREQUESTSTATE_BODY_START) {
        keys.clear();
        return 0;
    }

    while (! gs.bufferEmpty()) {
        char letter = gs.bufferGet();
        keys.put( letter );
    }

    if (state == GSwifi::GSREQUESTSTATE_RECEIVED) {
        int8_t result = keys.putDone();
        if (result != 0) {
            keys.clear();
            gs.writeHead(cid, 400);
        }
        else {
            keys.dump();
            keys.save();
            gs.writeHead(cid, 200);
        }

        gs.writeEnd();
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );

        if (result == 0) {
            gs.setRegDomain( keys.regdomain );
            ring_put( &commands, COMMAND_CONNECT );
        }
    }
}
static int8_t on_post_messages_request(int8_t cid, GSwifi::GSREQUESTSTATE state) {
    while (! gs.bufferEmpty()) {
        char letter = gs.bufferGet();
        parse_json( letter );
    }

    if (state == GSwifi::GSREQUESTSTATE_RECEIVED) {
        // should be xmitting or idle (xmit finished)
        if (IrCtrl.state == IR_WRITING) {
            HTTPLOG_PRINTLN("!E7");
            // invalid json
            gs.writeHead(cid, 400);
            gs.writeEnd();
        }
        else {
            gs.writeHead(cid, 200);
            gs.writeEnd();
        }
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );

#ifdef USE_INTERNET
        TIMER_START( suspend_polling_timer, SUSPEND_GET_MESSAGES_INTERVAL );
#endif
    }

    return 0;
}
static int8_t on_request(int8_t cid, int8_t routeid, GSwifi::GSREQUESTSTATE state) {
    if ( (state == GSwifi::GSREQUESTSTATE_RECEIVED) &&
         (! gs.validRequest()) &&
         (! has_valid_pass) ) {
        HTTPLOG_PRINTLN("!E32");
        gs.writeHead(cid, 400);
        gs.writeEnd();
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );
        return -1;
    }

    switch (routeid) {
    case 0: // POST /messages
        return on_post_messages_request(cid, state);

    case 1: // POST /keys
        // when client requests for a new key,
        // we request server for one, and respond to client with the result from server
        return on_post_keys_request(cid, state);

    case 2: // GET /messages
        return on_get_messages_request(cid, state);

    case 3: // POST /wifi
        return on_post_wifi_request(cid, state);

    default:
        break;
    }
    return -1;
}
int8_t irkit_httpclient_post_keys() {
    // devicekey=[0-9A-F]{32}
    char body[POST_KEYS_BODY_LENGTH+1];
    sprintf(body, "devicekey=%s", keys.getKey());
    int8_t result = gs.post( "/k",
                             body, POST_KEYS_BODY_LENGTH,
                             &on_post_keys_response,
                             10 );
    if ( result < 0 ) {
        gs.writeHead( post_keys_cid, 500 );
        gs.writeEnd();
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, post_keys_cid );
    }
}
static int8_t on_get_messages_request(int8_t cid, GSwifi::GSREQUESTSTATE state) {
    if (state != GSwifi::GSREQUESTSTATE_RECEIVED) {
        return -1;
    }

    gs.writeHead(cid, 200);

    if ( (IrCtrl.len <= 0) ||
         (IrCtrl.state != IR_RECVED_IDLE) ) {
        // if no data
        gs.writeEnd();
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );
        return 0;
    }

    IR_state( IR_READING );

    gs.write("{\"format\":\"raw\",\"freq\":"); // format fixed to "raw" for now
    gs.write(IrCtrl.freq);
    gs.write(",\"data\":[");
    for (uint16_t i=0; i<IrCtrl.len; i++) {
        gs.write( IR_get() );
        if (i != IrCtrl.len - 1) {
            gs.write(",");
        }
    }
    gs.write("]}");
    gs.writeEnd();
    ring_put( &commands, COMMAND_CLOSE );
    ring_put( &commands, cid );

    IR_state( IR_IDLE );

#ifdef USE_INTERNET
    TIMER_START( suspend_polling_timer, SUSPEND_GET_MESSAGES_INTERVAL );
#endif

    return 0;
}
static int8_t on_post_keys_response(int8_t cid, uint16_t status_code, GSwifi::GSREQUESTSTATE state) {
    HTTPLOG_PRINT(P("< P /k ")); HTTPLOG_PRINTLN(status_code);

    if (status_code != 200) {
        gs.bufferClear();
    }

    if (state != GSwifi::GSREQUESTSTATE_RECEIVED) {
        return 0;
    }

    gs.writeHead( post_keys_cid, status_code );

    switch (status_code) {
    case 200:
        while (! gs.bufferEmpty()) {
            char letter = gs.bufferGet();
            gs.write( letter );
        }
        gs.writeEnd();
        break;
    default:
        gs.writeEnd();
        break;
    }

    ring_put( &commands, COMMAND_CLOSE );
    ring_put( &commands, cid );
    ring_put( &commands, COMMAND_CLOSE );
    if (ring_isfull( &commands )) {
        HTTPLOG_PRINTLN("!E8");
        return -1;
    }
    ring_put( &commands, post_keys_cid );

    return 0;
}