示例#1
0
StreamReadStatus Stream::ReadLine(String *line, StreamReadContext& context, bool may_wait)
{
	if (context.Eof)
		return StatusEof;

	if (context.MustRead) {
		if (!context.FillFromStream(this, may_wait)) {
			context.Eof = true;

			*line = String(context.Buffer, &(context.Buffer[context.Size]));
			boost::algorithm::trim_right(*line);

			return StatusNewItem;
		}
	}

	for (size_t i = 0; i < context.Size; i++) {
		if (context.Buffer[i] == '\n') {
			*line = String(context.Buffer, context.Buffer + i);
			boost::algorithm::trim_right(*line);

			context.DropData(i + 1u);

			context.MustRead = !context.Size;
			return StatusNewItem;
		}
	}

	context.MustRead = true;
	return StatusNeedData;
}
示例#2
0
bool HttpRequest::Parse(StreamReadContext& src, bool may_wait)
{
	if (m_State != HttpRequestBody) {
		String line;
		StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);

		if (srs != StatusNewItem)
			return false;

		if (m_State == HttpRequestStart) {
			/* ignore trailing new-lines */
			if (line == "")
				return true;

			std::vector<String> tokens;
			boost::algorithm::split(tokens, line, boost::is_any_of(" "));
			Log(LogDebug, "HttpRequest")
			    << "line: " << line << ", tokens: " << tokens.size();
			if (tokens.size() != 3)
				BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
			RequestMethod = tokens[0];
			RequestUrl = new class Url(tokens[1]);

			if (tokens[2] == "HTTP/1.0")
				ProtocolVersion = HttpVersion10;
			else if (tokens[2] == "HTTP/1.1") {
				ProtocolVersion = HttpVersion11;
			} else
				BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));

			m_State = HttpRequestHeaders;
		} else if (m_State == HttpRequestHeaders) {
			if (line == "") {
				m_State = HttpRequestBody;

				/* we're done if the request doesn't contain a message body */
				if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding"))
					Complete = true;
				else
					m_Body = new FIFO();

				return true;

			} else {
				String::SizeType pos = line.FindFirstOf(":");
				if (pos == String::NPos)
					BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
				String key = line.SubStr(0, pos).ToLower().Trim();

				String value = line.SubStr(pos + 1).Trim();
				Headers->Set(key, value);

				if (key == "x-http-method-override")
					RequestMethod = value;
			}
		} else {
			VERIFY(!"Invalid HTTP request state.");
		}
	} else if (m_State == HttpRequestBody) {
		if (Headers->Get("transfer-encoding") == "chunked") {
			if (!m_ChunkContext)
				m_ChunkContext = boost::make_shared<ChunkReadContext>(src);

			char *data;
			size_t size;
			StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);

			if (srs != StatusNewItem)
				return false;

			Log(LogInformation, "HttpRequest")
			    << "Read " << size << " bytes";

			m_Body->Write(data, size);

			delete [] data;

			if (size == 0) {
				Complete = true;
				return true;
			}
		} else {
			if (src.Eof)
				BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));

			if (src.MustRead) {
				if (!src.FillFromStream(m_Stream, false)) {
					src.Eof = true;
					BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
				}

				src.MustRead = false;
			}

			size_t length_indicator = Convert::ToLong(Headers->Get("content-length"));

			if (src.Size < length_indicator) {
				src.MustRead = true;
				return false;
			}

			m_Body->Write(src.Buffer, length_indicator);
			src.DropData(length_indicator);
			Complete = true;
			return true;
		}
	}

	return true;
}
示例#3
0
bool HttpRequest::ParseBody(StreamReadContext& src, bool may_wait)
{
	if (!m_Stream)
		return false;

	if (m_State != HttpRequestBody)
		BOOST_THROW_EXCEPTION(std::runtime_error("Invalid HTTP state"));

	/* we're done if the request doesn't contain a message body */
	if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding")) {
		CompleteBody = true;
		return true;
	} else if (!m_Body)
		m_Body = new FIFO();

	if (Headers->Get("transfer-encoding") == "chunked") {
		if (!m_ChunkContext)
			m_ChunkContext = std::make_shared<ChunkReadContext>(std::ref(src));

		char *data;
		size_t size;
		StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);

		if (srs != StatusNewItem)
			return false;

		m_Body->Write(data, size);

		delete [] data;

		if (size == 0) {
			CompleteBody = true;
		}

		return true;
	}

	if (src.Eof)
		BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));

	if (src.MustRead) {
		if (!src.FillFromStream(m_Stream, false)) {
			src.Eof = true;
			BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));
		}

		src.MustRead = false;
	}

	long length_indicator_signed = Convert::ToLong(Headers->Get("content-length"));

	if (length_indicator_signed < 0)
		BOOST_THROW_EXCEPTION(std::invalid_argument("Content-Length must not be negative."));

	size_t length_indicator = length_indicator_signed;

	if (src.Size < length_indicator) {
		src.MustRead = true;
		return false;
	}

	m_Body->Write(src.Buffer, length_indicator);
	src.DropData(length_indicator);
	CompleteBody = true;
	return true;
}
示例#4
0
bool HttpResponse::Parse(StreamReadContext& src, bool may_wait)
{
	if (m_State != HttpResponseBody) {
		String line;
		StreamReadStatus srs = m_Stream->ReadLine(&line, src, may_wait);

		if (srs != StatusNewItem)
			return false;

		if (m_State == HttpResponseStart) {
			/* ignore trailing new-lines */
			if (line == "")
				return true;

			std::vector<String> tokens = line.Split(" ");
			Log(LogDebug, "HttpRequest")
				<< "line: " << line << ", tokens: " << tokens.size();
			if (tokens.size() < 2)
				BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP response (Status line)"));

			if (tokens[0] == "HTTP/1.0")
				ProtocolVersion = HttpVersion10;
			else if (tokens[0] == "HTTP/1.1") {
				ProtocolVersion = HttpVersion11;
			} else
				BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported HTTP version"));

			StatusCode = Convert::ToLong(tokens[1]);

			if (tokens.size() >= 3)
				StatusMessage = tokens[2]; // TODO: Join tokens[2..end]

			m_State = HttpResponseHeaders;
		} else if (m_State == HttpResponseHeaders) {
			if (!Headers)
				Headers = new Dictionary();

			if (line == "") {
				m_State = HttpResponseBody;
				m_Body = new FIFO();

				return true;

			} else {
				String::SizeType pos = line.FindFirstOf(":");
				if (pos == String::NPos)
					BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid HTTP request"));
				String key = line.SubStr(0, pos).ToLower().Trim();

				String value = line.SubStr(pos + 1).Trim();
				Headers->Set(key, value);
			}
		} else {
			VERIFY(!"Invalid HTTP request state.");
		}
	} else if (m_State == HttpResponseBody) {
		if (Headers->Get("transfer-encoding") == "chunked") {
			if (!m_ChunkContext)
				m_ChunkContext = std::make_shared<ChunkReadContext>(std::ref(src));

			char *data;
			size_t size;
			StreamReadStatus srs = HttpChunkedEncoding::ReadChunkFromStream(m_Stream, &data, &size, *m_ChunkContext.get(), may_wait);

			if (srs != StatusNewItem)
				return false;

			Log(LogNotice, "HttpResponse")
				<< "Read " << size << " bytes";

			m_Body->Write(data, size);

			delete[] data;

			if (size == 0) {
				Complete = true;
				return true;
			}
		} else {
			bool hasLengthIndicator = false;
			size_t lengthIndicator = 0;
			Value contentLengthHeader;

			if (Headers->Get("content-length", &contentLengthHeader)) {
				hasLengthIndicator = true;
				lengthIndicator = Convert::ToLong(contentLengthHeader);
			}

			if (!hasLengthIndicator && ProtocolVersion != HttpVersion10 && !Headers->Contains("transfer-encoding")) {
				Complete = true;
				return true;
			}

			if (hasLengthIndicator && src.Eof)
				BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body"));

			if (src.MustRead) {
				if (!src.FillFromStream(m_Stream, may_wait))
					src.Eof = true;

				src.MustRead = false;
			}

			if (!hasLengthIndicator)
				lengthIndicator = src.Size;

			if (src.Size < lengthIndicator) {
				src.MustRead = true;
				return may_wait;
			}

			m_Body->Write(src.Buffer, lengthIndicator);
			src.DropData(lengthIndicator);

			if (!hasLengthIndicator && !src.Eof) {
				src.MustRead = true;
				return may_wait;
			}

			Complete = true;
			return true;
		}
	}

	return true;
}
示例#5
0
/**
 * Reads data from a stream in netstring format.
 *
 * @param stream The stream to read from.
 * @param[out] str The String that has been read from the IOQueue.
 * @returns true if a complete String was read from the IOQueue, false otherwise.
 * @exception invalid_argument The input stream is invalid.
 * @see https://github.com/PeterScott/netstring-c/blob/master/netstring.c
 */
StreamReadStatus NetString::ReadStringFromStream(const Stream::Ptr& stream, String *str, StreamReadContext& context, bool may_wait)
{
	if (context.Eof)
		return StatusEof;

	if (context.MustRead) {
		if (!context.FillFromStream(stream, may_wait)) {
			context.Eof = true;
			return StatusEof;
		}

		context.MustRead = false;
	}

	size_t header_length = 0;

	for (size_t i = 0; i < context.Size; i++) {
		if (context.Buffer[i] == ':') {
			header_length = i;

			/* make sure there's a header */
			if (header_length == 0)
				BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (no length specifier)"));

			break;
		} else if (i > 16)
			BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing :)"));
	}

	if (header_length == 0) {
		context.MustRead = true;
		return StatusNeedData;
	}

	/* no leading zeros allowed */
	if (context.Buffer[0] == '0' && isdigit(context.Buffer[1]))
		BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (leading zero)"));

	size_t len, i;

	len = 0;
	for (i = 0; i < header_length && isdigit(context.Buffer[i]); i++) {
		/* length specifier must have at most 9 characters */
		if (i >= 9)
			BOOST_THROW_EXCEPTION(std::invalid_argument("Length specifier must not exceed 9 characters"));

		len = len * 10 + (context.Buffer[i] - '0');
	}

	/* read the whole message */
	size_t data_length = len + 1;

	char *data = context.Buffer + header_length + 1;

	if (context.Size < header_length + 1 + data_length) {
		context.MustRead = true;
		return StatusNeedData;
	}

	if (data[len] != ',')
		BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid NetString (missing ,)"));

	*str = String(&data[0], &data[len]);

	context.DropData(header_length + 1 + len + 1);

	return StatusNewItem;
}