| // 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/flip_server/flip_config.h" |
| #include "net/tools/flip_server/sm_connection.h" |
| #include "net/tools/flip_server/spdy_util.h" |
| #include "net/tools/flip_server/url_utilities.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(); |
| } |
| |
| 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_t 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_t 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_t stream_id, |
| uint32_t 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_t stream_id) { |
| SendEOFImpl(stream_id); |
| if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) { |
| sm_spdy_interface_->ResetForNewInterface(server_idx_); |
| } |
| } |
| |
| void HttpSM::SendErrorNotFound(uint32_t stream_id) { |
| SendErrorNotFoundImpl(stream_id); |
| } |
| |
| size_t HttpSM::SendSynStream(uint32_t stream_id, const BalsaHeaders& headers) { |
| return 0; |
| } |
| |
| size_t HttpSM::SendSynReply(uint32_t stream_id, const BalsaHeaders& headers) { |
| return SendSynReplyImpl(stream_id, headers); |
| } |
| |
| void HttpSM::SendDataFrame(uint32_t stream_id, |
| const char* data, |
| int64_t len, |
| uint32_t flags, |
| bool compress) { |
| SendDataFrameImpl(stream_id, data, len, flags, compress); |
| } |
| |
| void HttpSM::SendEOFImpl(uint32_t 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_t 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_t 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_t 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_t stream_id, |
| const char* data, |
| int64_t len, |
| uint32_t 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 |