예제 #1
0
파일: transferslot.cpp 프로젝트: Avin15/sdk
// file transfer state machine
void TransferSlot::doio(MegaClient* client)
{
    if (!fa)
    {
        // this is a pending completion, retry every 200 ms by default
        retrybt.backoff(2);
        retrying = true;

        return transfer->complete();
    }

    retrying = false;

    if (!tempurl.size())
    {
        return;
    }

    dstime backoff = 0;
    m_off_t p = 0;

    if (errorcount > 4)
    {
        return transfer->failed(API_EFAILED);
    }

    for (int i = connections; i--; )
    {
        if (reqs[i])
        {
            switch (reqs[i]->status)
            {
                case REQ_INFLIGHT:
                    p += reqs[i]->transferred(client);
                    break;

                case REQ_SUCCESS:
                    lastdata = Waiter::ds;

                    progresscompleted += reqs[i]->size;

                    if (transfer->type == PUT)
                    {
                        errorcount = 0;
                        transfer->failcount = 0;

                        // completed put transfers are signalled through the
                        // return of the upload token
                        if (reqs[i]->in.size())
                        {
                            if (reqs[i]->in.size() == NewNode::UPLOADTOKENLEN * 4 / 3)
                            {
                                if (Base64::atob(reqs[i]->in.data(), transfer->ultoken, NewNode::UPLOADTOKENLEN + 1)
                                    == NewNode::UPLOADTOKENLEN)
                                {
                                    memcpy(transfer->filekey, transfer->key.key, sizeof transfer->key.key);
                                    ((int64_t*)transfer->filekey)[2] = transfer->ctriv;
                                    ((int64_t*)transfer->filekey)[3] = macsmac(&transfer->chunkmacs);
                                    SymmCipher::xorblock(transfer->filekey + SymmCipher::KEYLENGTH, transfer->filekey);

                                    return transfer->complete();
                                }
                            }

                            progresscompleted -= reqs[i]->size;

                            // fail with returned error
                            return transfer->failed((error)atoi(reqs[i]->in.c_str()));
                        }
                    }
                    else
                    {
                        if (reqs[i]->size == reqs[i]->bufpos)
                        {
                            errorcount = 0;
                            transfer->failcount = 0;

                            reqs[i]->finalize(fa, &transfer->key, &transfer->chunkmacs, transfer->ctriv, 0, -1);

                            if (progresscompleted == transfer->size)
                            {
                                // verify meta MAC
                                if (!progresscompleted || (macsmac(&transfer->chunkmacs) == transfer->metamac))
                                {
                                    return transfer->complete();
                                }
                                else
                                {
                                    progresscompleted -= reqs[i]->size;
                                    return transfer->failed(API_EKEY);
                                }
                            }
                        }
                        else
                        {
                            progresscompleted -= reqs[i]->size;
                            errorcount++;
                            reqs[i]->status = REQ_PREPARED;
                            break;
                        }
                    }

                    reqs[i]->status = REQ_READY;
                    break;

                case REQ_FAILURE:
                    if (reqs[i]->httpstatus == 509)
                    {
                        if (reqs[i]->timeleft < 0)
                        {
                            int creqtag = client->reqtag;
                            client->reqtag = 0;
                            client->sendevent(99408, "Overquota without timeleft");
                            client->reqtag = creqtag;
                        }

                        LOG_warn << "Bandwidth overquota from storage server";
                        if (reqs[i]->timeleft > 0)
                        {
                            backoff = reqs[i]->timeleft * 10;
                        }
                        else
                        {
                            // default retry intervals
                            backoff = MegaClient::DEFAULT_BW_OVERQUOTA_BACKOFF_SECS * 10;
                        }

                        return transfer->failed(API_EOVERQUOTA, backoff);
                    }
                    else
                    {
                        if (!failure)
                        {
                            failure = true;
                            bool changeport = false;

                            if (transfer->type == GET && client->autodownport && !memcmp(tempurl.c_str(), "http:", 5))
                            {
                                LOG_debug << "Automatically changing download port";
                                client->usealtdownport = !client->usealtdownport;
                                changeport = true;
                            }
                            else if (transfer->type == PUT && client->autoupport && !memcmp(tempurl.c_str(), "http:", 5))
                            {
                                LOG_debug << "Automatically changing upload port";
                                client->usealtupport = !client->usealtupport;
                                changeport = true;
                            }

                            client->app->transfer_failed(transfer, API_EFAILED);
                            client->setchunkfailed(&reqs[i]->posturl);

                            if (changeport)
                            {
                                toggleport(reqs[i]);
                            }
                        }
                    }
                    reqs[i]->status = REQ_PREPARED;

                default:
                    ;
            }
        }

        if (!failure)
        {
            if (!reqs[i] || (reqs[i]->status == REQ_READY))
            {
                m_off_t npos = ChunkedHash::chunkceil(transfer->pos);

                if (npos > transfer->size)
                {
                    npos = transfer->size;
                }

                if ((npos > transfer->pos) || !transfer->size)
                {
                    if (!reqs[i])
                    {
                        reqs[i] = transfer->type == PUT ? (HttpReqXfer*)new HttpReqUL() : (HttpReqXfer*)new HttpReqDL();
                    }

                    string finaltempurl = tempurl;
                    if (transfer->type == GET && client->usealtdownport
                            && !memcmp(tempurl.c_str(), "http:", 5))
                    {
                        size_t index = tempurl.find("/", 8);
                        if(index != string::npos && tempurl.find(":", 8) == string::npos)
                        {
                            finaltempurl.insert(index, ":8080");
                        }
                    }

                    if (transfer->type == PUT && client->usealtupport
                            && !memcmp(tempurl.c_str(), "http:", 5))
                    {
                        size_t index = tempurl.find("/", 8);
                        if(index != string::npos && tempurl.find(":", 8) == string::npos)
                        {
                            finaltempurl.insert(index, ":8080");
                        }
                    }

                    if (reqs[i]->prepare(fa, finaltempurl.c_str(), &transfer->key,
                                         &transfer->chunkmacs, transfer->ctriv,
                                         transfer->pos, npos))
                    {
                        reqs[i]->status = REQ_PREPARED;
                        transfer->pos = npos;
                    }
                    else
                    {
                        LOG_warn << "Error preparing transfer: " << fa->retry;
                        if (!fa->retry)
                        {
                            return transfer->failed(API_EREAD);
                        }

                        // retry the read shortly
                        backoff = 2;
                    }
                }
                else if (reqs[i])
                {
                    reqs[i]->status = REQ_DONE;
                }
            }

            if (reqs[i] && (reqs[i]->status == REQ_PREPARED))
            {
                reqs[i]->post(client);
            }
        }
    }

    p += progresscompleted;

    if (p != progressreported)
    {
        progressreported = p;
        lastdata = Waiter::ds;

        progress();
    }

    if (Waiter::ds - lastdata >= XFERTIMEOUT && !failure)
    {
        failure = true;
        bool changeport = false;

        if (transfer->type == GET && client->autodownport && !memcmp(tempurl.c_str(), "http:", 5))
        {
            LOG_debug << "Automatically changing download port due to a timeout";
            client->usealtdownport = !client->usealtdownport;
            changeport = true;
        }
        else if (transfer->type == PUT && client->autoupport && !memcmp(tempurl.c_str(), "http:", 5))
        {
            LOG_debug << "Automatically changing upload port due to a timeout";
            client->usealtupport = !client->usealtupport;
            changeport = true;
        }

        client->app->transfer_failed(transfer, API_EFAILED);

        for (int i = connections; i--; )
        {
            if (reqs[i] && reqs[i]->status == REQ_INFLIGHT)
            {
                client->setchunkfailed(&reqs[i]->posturl);
                reqs[i]->disconnect();

                if (changeport)
                {
                    toggleport(reqs[i]);
                }

                reqs[i]->status = REQ_PREPARED;
            }
        }
    }

    if (!failure)
    {
        if (!backoff && (Waiter::ds - lastdata) < XFERTIMEOUT)
        {
            // no other backoff: check again at XFERMAXFAIL
            backoff = XFERTIMEOUT - (Waiter::ds - lastdata);
        }

        retrybt.backoff(backoff);
    }
}
예제 #2
0
// file transfer state machine
void TransferSlot::doio(MegaClient* client)
{
    if (!fa)
    {
        // this is a pending completion, retry every 200 ms by default
        retrybt.backoff(2);
        retrying = true;

        return transfer->complete();
    }

    retrying = false;

    if (!tempurl.size())
    {
        return;
    }

    time_t backoff = 0;
    m_off_t p = 0;

    if (errorcount > 4)
    {
        return transfer->failed(API_EFAILED);
    }

    for (int i = connections; i--; )
    {
        if (reqs[i])
        {
            switch (reqs[i]->status)
            {
                case REQ_INFLIGHT:
                    p += reqs[i]->transferred(client);
                    break;

                case REQ_SUCCESS:
                    lastdata = Waiter::ds;

                    progresscompleted += reqs[i]->size;

                    if (transfer->type == PUT)
                    {
                        errorcount = 0;

                        // completed put transfers are signalled through the
                        // return of the upload token
                        if (reqs[i]->in.size())
                        {
                            if (reqs[i]->in.size() == NewNode::UPLOADTOKENLEN * 4 / 3)
                            {
                                if (Base64::atob(reqs[i]->in.data(), transfer->ultoken, NewNode::UPLOADTOKENLEN + 1)
                                    == NewNode::UPLOADTOKENLEN)
                                {
                                    memcpy(transfer->filekey, transfer->key.key, sizeof transfer->key.key);
                                    ((int64_t*)transfer->filekey)[2] = transfer->ctriv;
                                    ((int64_t*)transfer->filekey)[3] = macsmac(&transfer->chunkmacs);
                                    SymmCipher::xorblock(transfer->filekey + SymmCipher::KEYLENGTH, transfer->filekey);

                                    return transfer->complete();
                                }
                            }

                            // fail with returned error
                            return transfer->failed((error)atoi(reqs[i]->in.c_str()));
                        }
                    }
                    else
                    {
                        if (reqs[i]->size == reqs[i]->bufpos)
                        {
                            errorcount = 0;

                            reqs[i]->finalize(fa, &transfer->key, &transfer->chunkmacs, transfer->ctriv, 0, -1);

                            if (progresscompleted == transfer->size)
                            {
                                // verify meta MAC
                                if (!progresscompleted || (macsmac(&transfer->chunkmacs) == transfer->metamac))
                                {
                                    return transfer->complete();
                                }
                                else
                                {
                                    return transfer->failed(API_EKEY);
                                }
                            }
                        }
                        else
                        {
                            errorcount++;
                            reqs[i]->status = REQ_PREPARED;
                            break;
                        }
                    }

                    reqs[i]->status = REQ_READY;
                    break;

                case REQ_FAILURE:
                    if (reqs[i]->httpstatus == 509)
                    {
                        client->app->transfer_limit(transfer);

                        // fixed ten-minute retry intervals
                        backoff = 6000;
                    }
                    else
                    {
                        if (!failure)
                        {
                            failure = true;
                            client->setchunkfailed(&reqs[i]->posturl);
                        }

                        reqs[i]->status = REQ_PREPARED;
                    }

                default:
                    ;
            }
        }

        if (!failure)
        {
            if (!reqs[i] || (reqs[i]->status == REQ_READY))
            {
                m_off_t npos = ChunkedHash::chunkceil(transfer->pos);

                if (npos > transfer->size)
                {
                    npos = transfer->size;
                }

                if ((npos > transfer->pos) || !transfer->size)
                {
                    if (!reqs[i])
                    {
                        reqs[i] = transfer->type == PUT ? (HttpReqXfer*)new HttpReqUL() : (HttpReqXfer*)new HttpReqDL();
                    }

                    if (reqs[i]->prepare(fa, tempurl.c_str(), &transfer->key,
                                         &transfer->chunkmacs, transfer->ctriv,
                                         transfer->pos, npos))
                    {
                        reqs[i]->status = REQ_PREPARED;
                        transfer->pos = npos;
                    }
                    else
                    {
                        if (!fa->retry)
                        {
                            return transfer->failed(API_EREAD);
                        }

                        // retry the read shortly
                        backoff = 2;
                    }
                }
                else if (reqs[i])
                {
                    reqs[i]->status = REQ_DONE;
                }
            }

            if (reqs[i] && (reqs[i]->status == REQ_PREPARED))
            {
                reqs[i]->post(client);
            }
        }
    }

    p += progresscompleted;

    if (p != progressreported)
    {
        progressreported = p;
        lastdata = Waiter::ds;

        progress();
    }

    if (Waiter::ds - lastdata >= XFERTIMEOUT && !failure)
    {
        failure = true;
        client->app->transfer_failed(transfer, API_EFAILED);

        for (int i = connections; i--; )
        {
            if (reqs[i])
            {
                client->setchunkfailed(&reqs[i]->posturl);
                reqs[i]->disconnect();
                reqs[i]->status = REQ_PREPARED;
            }
        }
    }

    if (!failure)
    {
        if (!backoff && (Waiter::ds - lastdata) < XFERTIMEOUT)
        {
            // no other backoff: check again at XFERMAXFAIL
            backoff = XFERTIMEOUT - (Waiter::ds - lastdata);
        }

        retrybt.backoff(backoff);
    }
}