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

Commit 1a541075 authored by Andreas Huber's avatar Andreas Huber Committed by Android (Google) Code Review
Browse files

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

parents e1f2d411 c3119330
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
@@ -22,7 +22,6 @@ LOCAL_SRC_FILES:= \
        FileSource.cpp                    \
        FLACExtractor.cpp                 \
        HTTPBase.cpp                      \
        HTTPStream.cpp                    \
        JPEGSource.cpp                    \
        MP3Extractor.cpp                  \
        MPEG2TSWriter.cpp                 \
@@ -36,13 +35,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