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;
}
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) {
            ring_put( &commands, COMMAND_SETREGDOMAIN );
            ring_put( &commands, keys.regdomain );
            ring_put( &commands, COMMAND_CONNECT );
        }
    }
}
// added by eqiglii 2015-12-13
int8_t irkit_httpclient_post_temperature_(uint8_t temperature) {
    // send http post request to LAN server  
    char path[82];
    sprintf(path, P("/proxy.php?url=https://irkitrestapi.appspot.com/_ah/api/southbound/v1/temperature?"));    // added "?" by eqiglii 2016-06-13

    char body[POST_TEMPERATURE_BODY_LENGTH+1+4]; // +4, due to change the two "&" to "%26", eqiglii, 2016-06-13
    
    // char array[6];
    // dtostrf(temperature,5, 2, array); // this function works, but takes too much memory KB
    // I know that the Arduino version of sprinf does not support floats, but we're sticking to INTs here so it is fine

    sprintf(body, "irkit_id=%s%ssignal_content=%2d%ssignal_name=%s", gs.hostname(),"%26", temperature,"%26", "temp");    // change the two "&" to "%26", eqiglii 2016-06-13
    
    int8_t cid = gs.post(path, body, POST_TEMPERATURE_BODY_LENGTH, &on_post_messages_response,50 );   
    //int8_t cid = gs.post("/_ah/api/southbound/v1/temperature?", body, POST_TEMPERATURE_BODY_LENGTH, &on_post_messages_response,50 );   // this never worked out!!! eqiglii 2016-06-14, don't try any more!!!
    
    if (cid == polling_cid) {
        // we're polling on this cid, and our response handler is registered with this cid.
        // we already overwritten the response handler, so restart everything.
        // HTTPLOG_PRINTLN("!E30");
        wifi_hardware_reset();
        return -1;
    }
    return cid;
}
static int8_t on_get_messages_response(int8_t cid, uint16_t status_code, GSwifi::GSREQUESTSTATE state) {
    HTTPLOG_PRINT(P("< G /m ")); HTTPLOG_PRINTLN(status_code);

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

    switch (status_code) {
    case 200:
        while (! gs.bufferEmpty()) {
            char letter = gs.bufferGet();

            parse_json( letter );
        }

        if (state == GSwifi::GSREQUESTSTATE_RECEIVED) {
            // should not be WRITING here, should be XMITTING or IDLE (xmit finished)
            if (IrCtrl.state == IR_WRITING) {
                // prevent from locking in WRITING state forever
                IR_state( IR_IDLE );
            }

            ring_put( &commands, COMMAND_CLOSE );
            ring_put( &commands, cid );
            if ((polling_cid == cid) || (polling_cid == CID_UNDEFINED)) {
                polling_cid = CID_UNDEFINED;
                ring_put( &commands, COMMAND_START_POLLING );
            }
            // if polling_cid != cid
            // there's already an ongoing polling request, so request again when that one finishes
        }
        break;
    case HTTP_STATUSCODE_CLIENT_TIMEOUT:
        polling_cid = CID_UNDEFINED;
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );
        irkit_httpclient_start_polling( 5 );
        break;
    case HTTP_STATUSCODE_DISCONNECT:
        polling_cid = CID_UNDEFINED;
        irkit_httpclient_start_polling( 5 );
        break;
    // heroku responds with 503 if longer than 30sec,
    // or when deploy occurs
    case 503:
    default:
        if (state == GSwifi::GSREQUESTSTATE_RECEIVED) {
            ring_put( &commands, COMMAND_CLOSE );
            ring_put( &commands, cid );
            irkit_httpclient_start_polling( 5 );
        }
        break;
    }

    return 0;
}
int8_t irkit_httpclient_post_door() {
#ifdef USE_INTERNET
    // devicekey=[0-9A-F]{32}&hostname=IRKit%%%%
    char body[POST_DOOR_BODY_LENGTH+1];
    sprintf(body, "devicekey=%s&hostname=%s", keys.getKey(), gs.hostname());
    return gs.post( "/d", body, POST_DOOR_BODY_LENGTH, &on_post_door_response, 50 );
#else
    on_post_door_response(CID_UNDEFINED, 200, GSwifi::GSREQUESTSTATE_RECEIVED);
#endif
}
int8_t irkit_httpclient_post_door() {
    // devicekey=[0-9A-F]{32}&hostname=IRKit%%%%
    // send http post request to LAN server  
    char path[75];
    sprintf(path, P("/proxy.php?url=https://irkitrestapi.appspot.com/_ah/api/southbound/v1/door?")); // path must end up with "?", by eqiglii 2016-06-13
    
    char body[POST_DOOR_BODY_LENGTH+1+2];    // +2, due to change from "&" to "%26", eqiglii, 2016-06-13
    sprintf(body, "devicekey=%s%shostname=%s", keys.getKey(),"%26",gs.hostname()); // in http url encoding, "&" must be replaced by "%26", by eqiglii 2016-06-13
    Serial.println (body); // print log, added by eqiglii
    //return gs.post( "/d", body, POST_DOOR_BODY_LENGTH, &on_post_door_response, 50 );
    //return gs.post( "/_ah/api/southbound/v1/door?", body, POST_DOOR_BODY_LENGTH, &on_post_door_response, 50 );
    return gs.post(path, body, POST_DOOR_BODY_LENGTH, &on_post_door_response, 50 );
}
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 );
    }
}
int8_t irkit_httpclient_get_messages() {
    // /m?devicekey=C7363FDA0F06406AB11C29BA41272AE3&newer_than=4294967295
    //char path[68];
    //sprintf(path, P("/m?devicekey=%s&newer_than=%ld"), keys.getKey(), newest_message_id);
    char path[109]; // added 29 chars path,12 chars messageid
    sprintf(path, P("/_ah/api/southbound/v1/messages?devicekey=%s&newer_than=%ld"), keys.getKey(), newest_message_id);
    return gs.get(path, &on_get_messages_response, 50);
}
int8_t irkit_httpclient_post_messages() {
    // post body is IR data, move devicekey parameter to query, for implementation simplicity
    // /p?devicekey=C7363FDA0F06406AB11C29BA41272AE3&freq=38
    char path[54];
    sprintf(path, P("/p?devicekey=%s&freq=%d"), keys.getKey(), IrCtrl.freq);
    return gs.postBinary( path,
                          (const char*)sharedbuffer, IR_packedlength(),
                          &on_post_messages_response,
                          10 );
}
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_post_door_response(int8_t cid, uint16_t status_code, GSwifi::GSREQUESTSTATE state) {
    HTTPLOG_PRINT(P("< P /d ")); HTTPLOG_PRINTLN(status_code);

    gs.bufferClear();

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

    switch (status_code) {
    case 200:
        keys.setKeyValid(true);
        // save only independent area, since sharedbuffer might be populated by IR or so.
        keys.save2();
        IR_state( IR_IDLE );

        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );
#ifdef USE_INTERNET
        ring_put( &commands, COMMAND_START_POLLING );
#endif

        on_irkit_ready();

        break;
#ifdef USE_INTERNET
    case 401:
    case HTTP_STATUSCODE_CLIENT_TIMEOUT:
        // keys have expired, we have to start listening for POST /wifi again
        keys.clear();
        keys.save();
        software_reset();

        break;
    case 400: // must be program bug, happens when there's no hostname parameter
    case 408:
    case 503: // heroku responds with 503 if longer than 30sec
    default:
        // retry again on next loop
        ring_put( &commands, COMMAND_CLOSE );
        ring_put( &commands, cid );
        ring_put( &commands, COMMAND_POST_DOOR );
        break;
#endif
    }

    return 0;
}
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_messages_response(int8_t cid, uint16_t status_code, GSwifi::GSREQUESTSTATE state) {
    HTTPLOG_PRINT(P("< P /m ")); HTTPLOG_PRINTLN(status_code);

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

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

    ring_put( &commands, COMMAND_CLOSE );
    ring_put( &commands, cid );

    return 0;
}
int8_t irkit_httpclient_post_messages_() {
    // post body is IR data, move devicekey parameter to query, for implementation simplicity
    // /p?devicekey=C7363FDA0F06406AB11C29BA41272AE3&freq=38
    char path[54];
    sprintf(path, P("/p?devicekey=%s&freq=%d"), keys.getKey(), IrCtrl.freq);
    int8_t cid = gs.postBinary( path,
                                (const char*)sharedbuffer, IR_packedlength(),
                                &on_post_messages_response,
                                10 );
    if (cid == polling_cid) {
        // we're polling on this cid, and our response handler is registered with this cid.
        // we already overwritten the response handler, so restart everything.
        // HTTPLOG_PRINTLN("!E30");
        wifi_hardware_reset();
        return -1;
    }
    return cid;
}
void irkit_httpserver_register_handler() {
    gs.clearRoutes();

    // 0
    gs.registerRoute( GSwifi::GSMETHOD_POST, P("/messages") );
    // 1
    gs.registerRoute( GSwifi::GSMETHOD_POST, P("/keys") );
    // 2
    gs.registerRoute( GSwifi::GSMETHOD_GET,  P("/messages") );
    // 3
    gs.registerRoute( GSwifi::GSMETHOD_POST, P("/wifi") );

    gs.setRequestHandler( &on_request );
}
static void on_json_data( uint8_t key, uint32_t value, char *pass ) {
    if ( IrCtrl.state != IR_WRITING ) {
        return;
    }

    switch (key) {
    case IrJsonParserDataKeyId:
        newest_message_id = value;
        break;
    case IrJsonParserDataKeyFreq:
        IrCtrl.freq = value;
        break;
    case IrJsonParserDataKeyData:
        IR_put( value );
        break;
    case IrJsonParserDataKeyPass:
        if (strncmp(pass, gs.password(), 10) == 0) {
            has_valid_pass = true;
        }
    default:
        break;
    }
}
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;
}
int8_t irkit_httpclient_get_messages() {
    // /m?devicekey=C7363FDA0F06406AB11C29BA41272AE3&newer_than=4294967295
    char path[70];
    sprintf(path, P("/m?devicekey=%s&newer_than=%ld"), keys.getKey(), newest_message_id);
    return gs.get(path, &on_get_messages_response, 50);
}
int8_t irkit_httpclient_post_door() {
    // devicekey=[0-9A-F]{32}&hostname=IRKit%%%%
    char body[POST_DOOR_BODY_LENGTH+1];
    sprintf(body, "devicekey=%s&hostname=%s", keys.getKey(), gs.hostname());
    return gs.post( "/d", body, POST_DOOR_BODY_LENGTH, &on_post_door_response, 50 );
}