// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/tools/flip_server/http_interface.h"
#include "net/tools/balsa/balsa_frame.h"
#include "net/tools/dump_cache/url_utilities.h"
#include "net/tools/flip_server/flip_config.h"
#include "net/tools/flip_server/sm_connection.h"
#include "net/tools/flip_server/spdy_util.h"
namespace net {
HttpSM::HttpSM(SMConnection* connection,
SMInterface* sm_spdy_interface,
MemoryCache* memory_cache,
FlipAcceptor* acceptor)
: http_framer_(new BalsaFrame),
stream_id_(0),
server_idx_(-1),
connection_(connection),
sm_spdy_interface_(sm_spdy_interface),
output_list_(connection->output_list()),
output_ordering_(connection),
memory_cache_(connection->memory_cache()),
acceptor_(acceptor) {
http_framer_->set_balsa_visitor(this);
http_framer_->set_balsa_headers(&headers_);
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY)
http_framer_->set_is_request(false);
}
HttpSM::~HttpSM() {
Reset();
delete http_framer_;
}
void HttpSM::ProcessBodyData(const char* input, size_t size) {
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process Body Data: stream "
<< stream_id_ << ": size " << size;
sm_spdy_interface_->SendDataFrame(stream_id_, input, size, 0, false);
}
}
void HttpSM::ProcessHeaders(const BalsaHeaders& headers) {
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
std::string host =
UrlUtilities::GetUrlHost(headers.GetHeader("Host").as_string());
std::string method = headers.request_method().as_string();
VLOG(1) << ACCEPTOR_CLIENT_IDENT
<< "Received Request: " << headers.request_uri().as_string() << " "
<< method;
std::string filename =
EncodeURL(headers.request_uri().as_string(), host, method);
NewStream(stream_id_, 0, filename);
stream_id_ += 2;
} else {
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Received Response from "
<< connection_->server_ip_ << ":" << connection_->server_port_
<< " ";
sm_spdy_interface_->SendSynReply(stream_id_, headers);
}
}
void HttpSM::MessageDone() {
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone. Sending EOF: "
<< "stream " << stream_id_;
sm_spdy_interface_->SendEOF(stream_id_);
} else {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: MessageDone.";
}
}
void HttpSM::HandleHeaderError(BalsaFrame* framer) { HandleError(); }
void HttpSM::HandleChunkingError(BalsaFrame* framer) { HandleError(); }
void HttpSM::HandleBodyError(BalsaFrame* framer) { HandleError(); }
void HttpSM::HandleError() {
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Error detected";
}
void HttpSM::AddToOutputOrder(const MemCacheIter& mci) {
output_ordering_.AddToOutputOrder(mci);
}
void HttpSM::InitSMInterface(SMInterface* sm_spdy_interface, int32 server_idx) {
sm_spdy_interface_ = sm_spdy_interface;
server_idx_ = server_idx;
}
void HttpSM::InitSMConnection(SMConnectionPoolInterface* connection_pool,
SMInterface* sm_interface,
EpollServer* epoll_server,
int fd,
std::string server_ip,
std::string server_port,
std::string remote_ip,
bool use_ssl) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Initializing server "
<< "connection.";
connection_->InitSMConnection(connection_pool,
sm_interface,
epoll_server,
fd,
server_ip,
server_port,
remote_ip,
use_ssl);
}
size_t HttpSM::ProcessReadInput(const char* data, size_t len) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process read input: stream "
<< stream_id_;
return http_framer_->ProcessInput(data, len);
}
size_t HttpSM::ProcessWriteInput(const char* data, size_t len) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process write input: size "
<< len << ": stream " << stream_id_;
char* dataPtr = new char[len];
memcpy(dataPtr, data, len);
DataFrame* data_frame = new DataFrame;
data_frame->data = dataPtr;
data_frame->size = len;
data_frame->delete_when_done = true;
connection_->EnqueueDataFrame(data_frame);
return len;
}
bool HttpSM::MessageFullyRead() const {
return http_framer_->MessageFullyRead();
}
void HttpSM::SetStreamID(uint32 stream_id) { stream_id_ = stream_id; }
bool HttpSM::Error() const { return http_framer_->Error(); }
const char* HttpSM::ErrorAsString() const {
return BalsaFrameEnums::ErrorCodeToString(http_framer_->ErrorCode());
}
void HttpSM::Reset() {
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Reset: stream " << stream_id_;
http_framer_->Reset();
}
void HttpSM::ResetForNewConnection() {
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Server connection closing "
<< "to: " << connection_->server_ip_ << ":"
<< connection_->server_port_ << " ";
}
// Message has not been fully read, either it is incomplete or the
// server is closing the connection to signal message end.
if (!MessageFullyRead()) {
VLOG(2) << "HTTP response closed before end of file detected. "
<< "Sending EOF to spdy.";
sm_spdy_interface_->SendEOF(stream_id_);
}
output_ordering_.Reset();
http_framer_->Reset();
if (sm_spdy_interface_) {
sm_spdy_interface_->ResetForNewInterface(server_idx_);
}
}
void HttpSM::Cleanup() {
if (!(acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER)) {
VLOG(2) << "HttpSM Request Fully Read; stream_id: " << stream_id_;
connection_->Cleanup("request complete");
}
}
int HttpSM::PostAcceptHook() { return 1; }
void HttpSM::NewStream(uint32 stream_id,
uint32 priority,
const std::string& filename) {
MemCacheIter mci;
mci.stream_id = stream_id;
mci.priority = priority;
if (!memory_cache_->AssignFileData(filename, &mci)) {
// error creating new stream.
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound";
SendErrorNotFound(stream_id);
} else {
AddToOutputOrder(mci);
}
}
void HttpSM::SendEOF(uint32 stream_id) {
SendEOFImpl(stream_id);
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) {
sm_spdy_interface_->ResetForNewInterface(server_idx_);
}
}
void HttpSM::SendErrorNotFound(uint32 stream_id) {
SendErrorNotFoundImpl(stream_id);
}
size_t HttpSM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
return 0;
}
size_t HttpSM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
return SendSynReplyImpl(stream_id, headers);
}
void HttpSM::SendDataFrame(uint32 stream_id,
const char* data,
int64 len,
uint32 flags,
bool compress) {
SendDataFrameImpl(stream_id, data, len, flags, compress);
}
void HttpSM::SendEOFImpl(uint32 stream_id) {
DataFrame* df = new DataFrame;
df->data = "0\r\n\r\n";
df->size = 5;
df->delete_when_done = false;
EnqueueDataFrame(df);
if (acceptor_->flip_handler_type_ == FLIP_HANDLER_HTTP_SERVER) {
Reset();
}
}
void HttpSM::SendErrorNotFoundImpl(uint32 stream_id) {
BalsaHeaders my_headers;
my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
my_headers.RemoveAllOfHeader("content-length");
my_headers.AppendHeader("transfer-encoding", "chunked");
SendSynReplyImpl(stream_id, my_headers);
SendDataFrame(stream_id, "page not found", 14, 0, false);
SendEOFImpl(stream_id);
output_ordering_.RemoveStreamId(stream_id);
}
size_t HttpSM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
SimpleBuffer sb;
headers.WriteHeaderAndEndingToBuffer(&sb);
DataFrame* df = new DataFrame;
df->size = sb.ReadableBytes();
char* buffer = new char[df->size];
df->data = buffer;
df->delete_when_done = true;
sb.Read(buffer, df->size);
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
<< stream_id_;
size_t df_size = df->size;
EnqueueDataFrame(df);
return df_size;
}
size_t HttpSM::SendSynStreamImpl(uint32 stream_id,
const BalsaHeaders& headers) {
SimpleBuffer sb;
headers.WriteHeaderAndEndingToBuffer(&sb);
DataFrame* df = new DataFrame;
df->size = sb.ReadableBytes();
char* buffer = new char[df->size];
df->data = buffer;
df->delete_when_done = true;
sb.Read(buffer, df->size);
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "Sending HTTP Reply header "
<< stream_id_;
size_t df_size = df->size;
EnqueueDataFrame(df);
return df_size;
}
void HttpSM::SendDataFrameImpl(uint32 stream_id,
const char* data,
int64 len,
uint32 flags,
bool compress) {
char chunk_buf[128];
snprintf(chunk_buf, sizeof(chunk_buf), "%x\r\n", (unsigned int)len);
std::string chunk_description(chunk_buf);
DataFrame* df = new DataFrame;
df->size = chunk_description.size() + len + 2;
char* buffer = new char[df->size];
df->data = buffer;
df->delete_when_done = true;
memcpy(buffer, chunk_description.data(), chunk_description.size());
memcpy(buffer + chunk_description.size(), data, len);
memcpy(buffer + chunk_description.size() + len, "\r\n", 2);
EnqueueDataFrame(df);
}
void HttpSM::EnqueueDataFrame(DataFrame* df) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Enqueue data frame: stream "
<< stream_id_;
connection_->EnqueueDataFrame(df);
}
void HttpSM::GetOutput() {
MemCacheIter* mci = output_ordering_.GetIter();
if (mci == NULL) {
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput: nothing to "
<< "output!?: stream " << stream_id_;
return;
}
if (!mci->transformed_header) {
mci->bytes_sent =
SendSynReply(mci->stream_id, *(mci->file_data->headers()));
mci->transformed_header = true;
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput transformed "
<< "header stream_id: [" << mci->stream_id << "]";
return;
}
if (mci->body_bytes_consumed >= mci->file_data->body().size()) {
SendEOF(mci->stream_id);
output_ordering_.RemoveStreamId(mci->stream_id);
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "GetOutput remove_stream_id: ["
<< mci->stream_id << "]";
return;
}
size_t num_to_write =
mci->file_data->body().size() - mci->body_bytes_consumed;
if (num_to_write > mci->max_segment_size)
num_to_write = mci->max_segment_size;
SendDataFrame(mci->stream_id,
mci->file_data->body().data() + mci->body_bytes_consumed,
num_to_write,
0,
true);
VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: GetOutput SendDataFrame["
<< mci->stream_id << "]: " << num_to_write;
mci->body_bytes_consumed += num_to_write;
mci->bytes_sent += num_to_write;
}
} // namespace net