LOCAL void set_state(CONNECTION_STATE state) {
	mConnectionState = state;
	if(state != CS_DISCONNECT) {
		if(dhmem_isblock()) {
			mNeedRecover = 1;
			return;
		}
	}
	switch(state) {
	case CS_DISCONNECT:
		start_resolve_dh_server();
		break;
	case CS_GETINFO:
	case CS_REGISTER:
	case CS_POLL:
	{
		const sint8 cr = espconn_connect(&mDHConnector);
		if(cr == ESPCONN_ISCONN)
			return;
		if(cr != ESPCONN_OK) {
			dhesperrors_espconn_result("Connector espconn_connect failed:", cr);
			arm_repeat_timer(RETRY_CONNECTION_INTERVAL_MS);
		}
		break;
	}
	default:
		dhdebug("ASSERT: set_state wrong state %d", mConnectionState);
	}
}
LOCAL void network_connect_cb(void *arg) {
	HTTP_REQUEST *request;
	switch(mConnectionState) {
	case CS_GETINFO:
		request = &mInfoRequest;
		dhdebug("Send info request...");
		break;
	case CS_REGISTER:
		request = &mRegisterRequest;
		dhdebug("Send register request...");
		break;
	case CS_POLL:
		request = &mPollRequest;
		dhdebug("Send poll request...");
		break;
	default:
		dhdebug("ASSERT: networkConnectCb wrong state %d", mConnectionState);
	}
	int res;
	if( (res = espconn_send(&mDHConnector, request->data, request->len)) != ESPCONN_OK) {
		mConnectionState = CS_DISCONNECT;
		dhesperrors_espconn_result("network_connect_cb failed:", res);
		espconn_disconnect(&mDHConnector);
	} else {
		dhstatistic_add_bytes_sent(request->len);
	}
}
LOCAL void ICACHE_FLASH_ATTR senderConnectCb(void *arg) {
	int res;
	if( (res = espconn_send(&mDHSender, mSenderRequest.data, mSenderRequest.len)) != ESPCONN_OK) {
		dhesperrors_espconn_result("sender espconn_send failed:", res);
		espconn_disconnect(&mDHSender);
	} else {
		dhstatistic_add_bytes_sent(mSenderRequest.len);
	}
}
LOCAL void ICACHE_FLASH_ATTR start_resolve_dh_server() {
	static ip_addr_t ip;
	const char *server = dhrequest_current_server();
	char host[os_strlen(server) + 1];
	const char *fr = server;
	while(*fr != ':') {
		fr++;
		if(*fr == 0) {
			fr = 0;
			break;
		}
	}
	if(fr) {
		fr++;
		if(*fr != '/')
			fr = 0;
	}
	if (fr) {
		while (*fr == '/')
			fr++;
		int i = 0;
		while (*fr != '/' && *fr != ':' && *fr != 0)
			host[i++] = *fr++;
		// read port if present
		int port = 0;
		if(*fr == ':') {
			unsigned char d;
			fr++;
			while ( (d = *fr - 0x30) < 10) {
				fr++;
				port = port*10 + d;
				if(port > 0xFFFF)
					break;
			}
		}
		if(port && port < 0xFFFF)
			mDHConnector.proto.tcp->remote_port = port;
		else if (os_strncmp(dhrequest_current_server(), "https", 5) == 0)
			mDHConnector.proto.tcp->remote_port = 443; // HTTPS default port
		else
			mDHConnector.proto.tcp->remote_port = 80; //HTTP default port
		host[i] = 0;
		dhdebug("Resolving %s", host);
		err_t r = espconn_gethostbyname(&mDHConnector, host, &ip, resolve_cb);
		if(r == ESPCONN_OK) {
			resolve_cb(host, &ip, NULL);
		} else if(r != ESPCONN_INPROGRESS) {
			dhesperrors_espconn_result("Resolving failed:", r);
			arm_repeat_timer(RETRY_CONNECTION_INTERVAL_MS);
		}
	} else {
		dhdebug("Can not find scheme in server url");
	}
}
LOCAL void ICACHE_FLASH_ATTR dhsender_next(void *arg) {
	if(mStopped)
		return;
	if(mSenderTook == 0) {
		if(dhsender_queue_take(&mSenderRequest, &isCurrentNotification))
			mSenderTook = DHSENDER_RETRY_COUNT;
		else
			return;
	}
	sint8 cr = espconn_connect(&mDHSender);
	if(cr == ESPCONN_ISCONN) {
		return;
	} else if (cr != ESPCONN_OK) {
		dhesperrors_espconn_result("Sender espconn_connect failed:", cr);
		dhsender_arm_timer(RETRY_CONNECTION_INTERVAL_MS);
	} else {
		dhdebug("Sender start");
	}
}
LOCAL void ICACHE_FLASH_ATTR senderErrorCb(void *arg, sint8 err) {
	dhesperrors_espconn_result("Sender error occurred:", err);
	decrementSenderTook();
	dhsender_arm_timer(RETRY_CONNECTION_INTERVAL_MS);
	dhstatistic_inc_network_errors_count();
}
LOCAL void ICACHE_FLASH_ATTR check_send_res(sint8 res) {
	if(res)
		dhesperrors_espconn_result("espconn_send returned: %d", res);
}
LOCAL void network_error_cb(void *arg, sint8 err) {
	dhesperrors_espconn_result("Connector error occurred:", err);
	mConnectionState = CS_DISCONNECT;
	arm_repeat_timer(RETRY_CONNECTION_INTERVAL_MS);
	dhstatistic_inc_network_errors_count();
}