blob: 049c9ad9f6c56e2b3064737d8e2b2e6d85a4b08e [file] [log] [blame]
/*
* Copyright 2012, 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.
*/
#include "ParsedMessage.h"
#include <ctype.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/hexdump.h>
namespace android {
// static
sp<ParsedMessage> ParsedMessage::Parse(
const char *data, size_t size, bool noMoreData, size_t *length) {
sp<ParsedMessage> msg = new ParsedMessage;
ssize_t res = msg->parse(data, size, noMoreData);
if (res < 0) {
*length = 0;
return NULL;
}
*length = res;
return msg;
}
ParsedMessage::ParsedMessage() {
}
ParsedMessage::~ParsedMessage() {
}
bool ParsedMessage::findString(const char *name, AString *value) const {
AString key = name;
key.tolower();
ssize_t index = mDict.indexOfKey(key);
if (index < 0) {
value->clear();
return false;
}
*value = mDict.valueAt(index);
return true;
}
bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
AString stringValue;
if (!findString(name, &stringValue)) {
return false;
}
char *end;
*value = strtol(stringValue.c_str(), &end, 10);
if (end == stringValue.c_str() || *end != '\0') {
*value = 0;
return false;
}
return true;
}
const char *ParsedMessage::getContent() const {
return mContent.c_str();
}
ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
if (size == 0) {
return -1;
}
ssize_t lastDictIndex = -1;
size_t offset = 0;
bool headersComplete = false;
while (offset < size) {
size_t lineEndOffset = offset;
while (lineEndOffset + 1 < size
&& (data[lineEndOffset] != '\r'
|| data[lineEndOffset + 1] != '\n')) {
++lineEndOffset;
}
if (lineEndOffset + 1 >= size) {
return -1;
}
AString line(&data[offset], lineEndOffset - offset);
if (offset == 0) {
// Special handling for the request/status line.
mDict.add(AString("_"), line);
offset = lineEndOffset + 2;
continue;
}
if (lineEndOffset == offset) {
// An empty line separates headers from body.
headersComplete = true;
offset += 2;
break;
}
if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
// Support for folded header values.
if (lastDictIndex >= 0) {
// Otherwise it's malformed since the first header line
// cannot continue anything...
AString &value = mDict.editValueAt(lastDictIndex);
value.append(line);
}
offset = lineEndOffset + 2;
continue;
}
ssize_t colonPos = line.find(":");
if (colonPos >= 0) {
AString key(line, 0, colonPos);
key.trim();
key.tolower();
line.erase(0, colonPos + 1);
lastDictIndex = mDict.add(key, line);
}
offset = lineEndOffset + 2;
}
if (!headersComplete && (!noMoreData || offset == 0)) {
// We either saw the empty line separating headers from body
// or we saw at least the status line and know that no more data
// is going to follow.
return -1;
}
for (size_t i = 0; i < mDict.size(); ++i) {
mDict.editValueAt(i).trim();
}
int32_t contentLength;
if (!findInt32("content-length", &contentLength) || contentLength < 0) {
contentLength = 0;
}
size_t totalLength = offset + contentLength;
if (size < totalLength) {
return -1;
}
mContent.setTo(&data[offset], contentLength);
return totalLength;
}
bool ParsedMessage::getRequestField(size_t index, AString *field) const {
AString line;
CHECK(findString("_", &line));
size_t prevOffset = 0;
size_t offset = 0;
for (size_t i = 0; i <= index; ++i) {
if (offset >= line.size()) {
return false;
}
ssize_t spacePos = line.find(" ", offset);
if (spacePos < 0) {
spacePos = line.size();
}
prevOffset = offset;
offset = spacePos + 1;
}
field->setTo(line, prevOffset, offset - prevOffset - 1);
return true;
}
bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
AString statusCodeString;
if (!getRequestField(1, &statusCodeString)) {
*statusCode = 0;
return false;
}
char *end;
*statusCode = strtol(statusCodeString.c_str(), &end, 10);
if (*end != '\0' || end == statusCodeString.c_str()
|| (*statusCode) < 100 || (*statusCode) > 999) {
*statusCode = 0;
return false;
}
return true;
}
AString ParsedMessage::debugString() const {
AString line;
CHECK(findString("_", &line));
line.append("\n");
for (size_t i = 0; i < mDict.size(); ++i) {
const AString &key = mDict.keyAt(i);
const AString &value = mDict.valueAt(i);
if (key == AString("_")) {
continue;
}
line.append(key);
line.append(": ");
line.append(value);
line.append("\n");
}
line.append("\n");
line.append(mContent);
return line;
}
// static
bool ParsedMessage::GetAttribute(
const char *s, const char *key, AString *value) {
value->clear();
size_t keyLen = strlen(key);
for (;;) {
while (isspace(*s)) {
++s;
}
const char *colonPos = strchr(s, ';');
size_t len =
(colonPos == NULL) ? strlen(s) : colonPos - s;
if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
value->setTo(&s[keyLen + 1], len - keyLen - 1);
return true;
}
if (colonPos == NULL) {
return false;
}
s = colonPos + 1;
}
}
// static
bool ParsedMessage::GetInt32Attribute(
const char *s, const char *key, int32_t *value) {
AString stringValue;
if (!GetAttribute(s, key, &stringValue)) {
*value = 0;
return false;
}
char *end;
*value = strtol(stringValue.c_str(), &end, 10);
if (end == stringValue.c_str() || *end != '\0') {
*value = 0;
return false;
}
return true;
}
} // namespace android