Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3119330 authored by Andreas Huber's avatar Andreas Huber
Browse files

Remove legacy http support from stagefright, chromium is the new hotness.

Change-Id: I6725d42d38b91e6a1cbca43174870f445aeb3d99
parent 67d464f4
Loading
Loading
Loading
Loading
+0 −61
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SHOUTCAST_SOURCE_H_

#define SHOUTCAST_SOURCE_H_

#include <sys/types.h>

#include <media/stagefright/MediaSource.h>

namespace android {

class HTTPStream;
class MediaBufferGroup;

class ShoutcastSource : public MediaSource {
public:
    // Assumes ownership of "http".
    ShoutcastSource(HTTPStream *http);

    virtual status_t start(MetaData *params = NULL);
    virtual status_t stop();

    virtual sp<MetaData> getFormat();

    virtual status_t read(
            MediaBuffer **buffer, const ReadOptions *options = NULL);

protected:
    virtual ~ShoutcastSource();

private:
    HTTPStream *mHttp;
    size_t mMetaDataOffset;
    size_t mBytesUntilMetaData;

    MediaBufferGroup *mGroup;
    bool mStarted;

    ShoutcastSource(const ShoutcastSource &);
    ShoutcastSource &operator= (const ShoutcastSource &);
};

}  // namespace android

#endif  // SHOUTCAST_SOURCE_H_
+0 −3
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ LOCAL_SRC_FILES:= \
        FileSource.cpp                    \
        FLACExtractor.cpp                 \
        HTTPBase.cpp                      \
        HTTPStream.cpp                    \
        JPEGSource.cpp                    \
        MP3Extractor.cpp                  \
        MPEG2TSWriter.cpp                 \
@@ -38,13 +37,11 @@ LOCAL_SRC_FILES:= \
        MediaSourceSplitter.cpp           \
        MetaData.cpp                      \
        NuCachedSource2.cpp               \
        NuHTTPDataSource.cpp              \
        OMXClient.cpp                     \
        OMXCodec.cpp                      \
        OggExtractor.cpp                  \
        SampleIterator.cpp                \
        SampleTable.cpp                   \
        ShoutcastSource.cpp               \
        StagefrightMediaScanner.cpp       \
        StagefrightMetadataRetriever.cpp  \
        ThrottledSource.cpp               \
+12 −7
Original line number Diff line number Diff line
@@ -24,10 +24,11 @@
#include "include/ChromiumHTTPDataSource.h"
#endif

#include "include/NuHTTPDataSource.h"

#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>

#include <cutils/properties.h>
#include <cutils/qtaguid.h>

namespace android {

@@ -44,14 +45,12 @@ HTTPBase::HTTPBase()
// static
sp<HTTPBase> HTTPBase::Create(uint32_t flags) {
#if CHROMIUM_AVAILABLE
    char value[PROPERTY_VALUE_MAX];
    if (!property_get("media.stagefright.use-chromium", value, NULL)
            || (strcasecmp("false", value) && strcmp("0", value))) {
        return new ChromiumHTTPDataSource(flags);
    } else
#endif
    {
        return new NuHTTPDataSource(flags);
        TRESPASS();

        return NULL;
    }
}

@@ -135,4 +134,10 @@ bool HTTPBase::getUID(uid_t *uid) const {
    return true;
}

// static
void HTTPBase::RegisterSocketUser(int s, uid_t uid) {
    static const uint32_t kTag = 0xdeadbeef;
    set_qtaguid(s, kTag, uid);
}

}  // namespace android
+0 −623
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "HTTPStream"
#include <utils/Log.h>

#include "include/HTTPStream.h"

#include <sys/socket.h>

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <media/stagefright/foundation/ADebug.h>

#include <openssl/ssl.h>

namespace android {

// static
const char *HTTPStream::kStatusKey = ":status:";  // MUST be lowercase.

HTTPStream::HTTPStream()
    : mState(READY),
      mUIDValid(false),
      mSocket(-1),
      mSSLContext(NULL),
      mSSL(NULL) {
}

HTTPStream::~HTTPStream() {
    disconnect();

    if (mSSLContext != NULL) {
        SSL_CTX_free((SSL_CTX *)mSSLContext);
        mSSLContext = NULL;
    }
}

void HTTPStream::setUID(uid_t uid) {
    mUIDValid = true;
    mUID = uid;
}

static bool MakeSocketBlocking(int s, bool blocking) {
    // Make socket non-blocking.
    int flags = fcntl(s, F_GETFL, 0);
    if (flags == -1) {
        return false;
    }

    if (blocking) {
        flags &= ~O_NONBLOCK;
    } else {
        flags |= O_NONBLOCK;
    }

    return fcntl(s, F_SETFL, flags) != -1;
}

static status_t MyConnect(
        int s, const struct sockaddr *addr, socklen_t addrlen) {
    status_t result = UNKNOWN_ERROR;

    MakeSocketBlocking(s, false);

    if (connect(s, addr, addrlen) == 0) {
        result = OK;
    } else if (errno != EINPROGRESS) {
        result = -errno;
    } else {
        for (;;) {
            fd_set rs, ws;
            FD_ZERO(&rs);
            FD_ZERO(&ws);
            FD_SET(s, &rs);
            FD_SET(s, &ws);

            struct timeval tv;
            tv.tv_sec = 0;
            tv.tv_usec = 100000ll;

            int nfds = ::select(s + 1, &rs, &ws, NULL, &tv);

            if (nfds < 0) {
                if (errno == EINTR) {
                    continue;
                }

                result = -errno;
                break;
            }

            if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) {
                result = OK;
                break;
            }

            if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
                // Get the pending error.
                int error = 0;
                socklen_t errorLen = sizeof(error);
                if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) {
                    // Couldn't get the real error, so report why not.
                    result = -errno;
                } else {
                    result = -error;
                }
                break;
            }

            // Timeout expired. Try again.
        }
    }

    MakeSocketBlocking(s, true);

    return result;
}

// Apparently under out linux closing a socket descriptor from one thread
// will not unblock a pending send/recv on that socket on another thread.
static ssize_t MySendReceive(
        int s, void *data, size_t size, int flags, bool sendData) {
    ssize_t result = 0;

    if (s < 0) {
        return -1;
    }
    while (size > 0) {
        fd_set rs, ws, es;
        FD_ZERO(&rs);
        FD_ZERO(&ws);
        FD_ZERO(&es);
        FD_SET(s, sendData ? &ws : &rs);
        FD_SET(s, &es);

        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 100000ll;

        int nfds = ::select(
                s + 1,
                sendData ? NULL : &rs,
                sendData ? &ws : NULL,
                &es,
                &tv);

        if (nfds < 0) {
            if (errno == EINTR) {
                continue;
            }

            result = -errno;
            break;
        } else if (nfds == 0) {
            // timeout

            continue;
        }

        CHECK_EQ(nfds, 1);

        ssize_t nbytes =
            sendData ? send(s, data, size, flags) : recv(s, data, size, flags);

        if (nbytes < 0) {
            if (errno == EINTR) {
                continue;
            }

            result = -errno;
            break;
        } else if (nbytes == 0) {
            result = 0;
            break;
        }

        data = (uint8_t *)data + nbytes;
        size -= nbytes;

        result = nbytes;
        break;
    }

    return result;
}

static ssize_t MySend(int s, const void *data, size_t size, int flags) {
    return MySendReceive(
            s, const_cast<void *>(data), size, flags, true /* sendData */);
}

static ssize_t MyReceive(int s, void *data, size_t size, int flags) {
    return MySendReceive(s, data, size, flags, false /* sendData */);
}

status_t HTTPStream::connect(const char *server, int port, bool https) {
    if (port < 0) {
        port = https ? 443 : 80;
    }

    Mutex::Autolock autoLock(mLock);

    status_t err = OK;

    if (mState == CONNECTED) {
        return ERROR_ALREADY_CONNECTED;
    }

    if (port < 0 || port > (int) USHRT_MAX) {
        return UNKNOWN_ERROR;
    }

    char service[sizeof("65536")];
    sprintf(service, "%d", port);
    struct addrinfo hints, *ai;
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
    hints.ai_socktype = SOCK_STREAM;

    int ret = getaddrinfo(server, service, &hints, &ai);
    if (ret) {
        return ERROR_UNKNOWN_HOST;
    }

    CHECK_EQ(mSocket, -1);

    mState = CONNECTING;
    status_t res = -1;
    struct addrinfo *tmp;
    for (tmp = ai; tmp; tmp = tmp->ai_next) {
        mSocket = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
        if (mSocket < 0) {
            continue;
        }

        if (mUIDValid) {
            RegisterSocketUser(mSocket, mUID);
        }

        setReceiveTimeout(30);  // Time out reads after 30 secs by default.

        int s = mSocket;

        mLock.unlock();

        res = MyConnect(s, tmp->ai_addr, tmp->ai_addrlen);

        mLock.lock();

        if (mState != CONNECTING) {
            close(s);
            freeaddrinfo(ai);
            return UNKNOWN_ERROR;
        }

        if (res == OK) {
            break;
        }

        close(s);
    }

    freeaddrinfo(ai);

    if (res != OK) {
        close(mSocket);
        mSocket = -1;

        mState = READY;
        return res;
    }

    if (https) {
        CHECK(mSSL == NULL);

        if (mSSLContext == NULL) {
            SSL_library_init();

            mSSLContext = SSL_CTX_new(TLSv1_client_method());

            if (mSSLContext == NULL) {
                LOGE("failed to create SSL context");
                mState = READY;
                return ERROR_IO;
            }
        }

        mSSL = SSL_new((SSL_CTX *)mSSLContext);

        if (mSSL == NULL) {
            LOGE("failed to create SSL session");

            mState = READY;
            return ERROR_IO;
        }

        int res = SSL_set_fd((SSL *)mSSL, mSocket);

        if (res == 1) {
            res = SSL_connect((SSL *)mSSL);
        }

        if (res != 1) {
            SSL_free((SSL *)mSSL);
            mSSL = NULL;

            LOGE("failed to connect over SSL");
            mState = READY;

            return ERROR_IO;
        }
    }

    mState = CONNECTED;

    return OK;
}

status_t HTTPStream::disconnect() {
    Mutex::Autolock autoLock(mLock);

    if (mState != CONNECTED && mState != CONNECTING) {
        return ERROR_NOT_CONNECTED;
    }

    if (mSSL != NULL) {
        SSL_shutdown((SSL *)mSSL);

        SSL_free((SSL *)mSSL);
        mSSL = NULL;
    }

    CHECK(mSocket >= 0);
    close(mSocket);
    mSocket = -1;

    mState = READY;

    return OK;
}

status_t HTTPStream::send(const char *data, size_t size) {
    if (mState != CONNECTED) {
        return ERROR_NOT_CONNECTED;
    }

    while (size > 0) {
        ssize_t n;
        if (mSSL != NULL) {
            n = SSL_write((SSL *)mSSL, data, size);

            if (n < 0) {
                n = -SSL_get_error((SSL *)mSSL, n);
            }
        } else {
            n = MySend(mSocket, data, size, 0);
        }

        if (n < 0) {
            disconnect();

            return n;
        } else if (n == 0) {
            disconnect();

            return ERROR_CONNECTION_LOST;
        }

        size -= (size_t)n;
        data += (size_t)n;
    }

    return OK;
}

status_t HTTPStream::send(const char *data) {
    return send(data, strlen(data));
}

// A certain application spawns a local webserver that sends invalid responses,
// specifically it terminates header line with only a newline instead of the
// CRLF (carriage-return followed by newline) required by the HTTP specs.
// The workaround accepts both behaviours but could potentially break
// legitimate responses that use a single newline to "fold" headers, which is
// why it's not yet on by default.
#define WORKAROUND_FOR_MISSING_CR       1

status_t HTTPStream::receive_line(char *line, size_t size) {
    if (mState != CONNECTED) {
        return ERROR_NOT_CONNECTED;
    }

    bool saw_CR = false;
    size_t length = 0;

    for (;;) {
        char c;
        ssize_t n;
        if (mSSL != NULL) {
            n = SSL_read((SSL *)mSSL, &c, 1);

            if (n < 0) {
                n = -SSL_get_error((SSL *)mSSL, n);
            }
        } else {
            n = MyReceive(mSocket, &c, 1, 0);
        }

        if (n < 0) {
            disconnect();

            return ERROR_IO;
        } else if (n == 0) {
            disconnect();

            return ERROR_CONNECTION_LOST;
        }

#if WORKAROUND_FOR_MISSING_CR
        if (c == '\n') {
            // We have a complete line.

            line[saw_CR ? length - 1 : length] = '\0';
            return OK;
        }
#else
        if (saw_CR &&  c == '\n') {
            // We have a complete line.

            line[length - 1] = '\0';
            return OK;
        }
#endif

        saw_CR = (c == '\r');

        if (length + 1 >= size) {
            return ERROR_MALFORMED;
        }
        line[length++] = c;
    }
}

status_t HTTPStream::receive_header(int *http_status) {
    *http_status = -1;
    mHeaders.clear();

    char line[2048];
    status_t err = receive_line(line, sizeof(line));
    if (err != OK) {
        return err;
    }

    mHeaders.add(AString(kStatusKey), AString(line));

    char *spacePos = strchr(line, ' ');
    if (spacePos == NULL) {
        // Malformed response?
        return UNKNOWN_ERROR;
    }

    char *status_start = spacePos + 1;
    char *status_end = status_start;
    while (isdigit(*status_end)) {
        ++status_end;
    }

    if (status_end == status_start) {
        // Malformed response, status missing?
        return UNKNOWN_ERROR;
    }

    memmove(line, status_start, status_end - status_start);
    line[status_end - status_start] = '\0';

    long tmp = strtol(line, NULL, 10);
    if (tmp < 0 || tmp > 999) {
        return UNKNOWN_ERROR;
    }

    *http_status = (int)tmp;

    for (;;) {
        err = receive_line(line, sizeof(line));
        if (err != OK) {
            return err;
        }

        if (*line == '\0') {
            // Empty line signals the end of the header.
            break;
        }

        // puts(line);

        char *colonPos = strchr(line, ':');
        if (colonPos == NULL) {
            AString key = line;
            key.tolower();

            mHeaders.add(key, AString());
        } else {
            char *end_of_key = colonPos;
            while (end_of_key > line && isspace(end_of_key[-1])) {
                --end_of_key;
            }

            char *start_of_value = colonPos + 1;
            while (isspace(*start_of_value)) {
                ++start_of_value;
            }

            *end_of_key = '\0';

            AString key = line;
            key.tolower();

            mHeaders.add(key, AString(start_of_value));
        }
    }

    return OK;
}

ssize_t HTTPStream::receive(void *data, size_t size) {
    size_t total = 0;
    while (total < size) {
        ssize_t n;
        if (mSSL != NULL) {
            n = SSL_read((SSL *)mSSL, (char *)data + total, size - total);

            if (n < 0) {
                n = -SSL_get_error((SSL *)mSSL, n);
            }
        } else {
            n = MyReceive(mSocket, (char *)data + total, size - total, 0);
        }

        if (n < 0) {
            LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n));

            disconnect();
            return (ssize_t)ERROR_IO;
        } else if (n == 0) {
            disconnect();

            LOGE("recv failed, server is gone, total received: %d bytes",
                 total);

            return total == 0 ? (ssize_t)ERROR_CONNECTION_LOST : total;
        }

        total += (size_t)n;
    }

    return (ssize_t)total;
}

bool HTTPStream::find_header_value(const AString &key, AString *value) const {
    AString key_lower = key;
    key_lower.tolower();

    ssize_t index = mHeaders.indexOfKey(key_lower);
    if (index < 0) {
        value->clear();
        return false;
    }

    *value = mHeaders.valueAt(index);

    return true;
}

void HTTPStream::setReceiveTimeout(int seconds) {
    if (seconds < 0) {
        // Disable the timeout.
        seconds = 0;
    }

    struct timeval tv;
    tv.tv_usec = 0;
    tv.tv_sec = seconds;
    CHECK_EQ(0, setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)));
}

// static
void HTTPStream::RegisterSocketUser(int s, uid_t uid) {
    // Lower bits MUST be 0.
    static const uint64_t kTag = 0xdeadbeef00000000ll;

    AString line = StringPrintf("t %d %llu %d", s, kTag, uid);

    int fd = open("/proc/net/xt_qtaguid/ctrl", O_WRONLY);
    write(fd, line.c_str(), line.size());
    close(fd);
    fd = -1;
}

}  // namespace android
+0 −560

File deleted.

Preview size limit exceeded, changes collapsed.

Loading