/*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::SendResponseBody +---------------------------------------------------------------------*/ NPT_Result PLT_HttpServerSocketTask::SendResponseBody(NPT_HttpResponse* response, NPT_OutputStream& output_stream) { NPT_HttpEntity* entity = response->GetEntity(); if (!entity) return NPT_SUCCESS; NPT_InputStreamReference body_stream; entity->GetInputStream(body_stream); if (body_stream.IsNull()) return NPT_SUCCESS; // check for chunked transfer encoding NPT_OutputStream* dest = &output_stream; if (entity->GetTransferEncoding() == NPT_HTTP_TRANSFER_ENCODING_CHUNKED) { dest = new NPT_HttpChunkedOutputStream(output_stream); } // send body NPT_LOG_FINE_1("sending body stream, %lld bytes", entity->GetContentLength()); NPT_LargeSize bytes_written = 0; NPT_Result result = NPT_StreamToStreamCopy(*body_stream, *dest, 0, entity->GetContentLength(), &bytes_written); /* passing 0 if content length is unknown will read until nothing is left */ if (NPT_FAILED(result)) { NPT_LOG_FINE_3("body stream only partially sent, %lld bytes (%d:%s)", bytes_written, result, NPT_ResultText(result)); } // flush to write out any buffered data left in chunked output if used dest->Flush(); // cleanup (this will send zero size chunk followed by CRLF) if (dest != &output_stream) delete dest; return result; }
/*---------------------------------------------------------------------- | PLT_HttpServerSocketTask::SendResponseHeaders +---------------------------------------------------------------------*/ NPT_Result PLT_HttpServerSocketTask::SendResponseHeaders(NPT_HttpResponse* response, NPT_OutputStream& output_stream, bool& keep_alive) { // add any headers that may be missing NPT_HttpHeaders& headers = response->GetHeaders(); // get the request entity to set additional headers NPT_InputStreamReference body_stream; NPT_HttpEntity* entity = response->GetEntity(); if (entity && NPT_SUCCEEDED(entity->GetInputStream(body_stream))) { // set the content length if known if (entity->ContentLengthIsKnown()) { headers.SetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH, NPT_String::FromIntegerU(entity->GetContentLength())); } // content type NPT_String content_type = entity->GetContentType(); if (!content_type.IsEmpty()) { headers.SetHeader(NPT_HTTP_HEADER_CONTENT_TYPE, content_type); } // content encoding NPT_String content_encoding = entity->GetContentEncoding(); if (!content_encoding.IsEmpty()) { headers.SetHeader(NPT_HTTP_HEADER_CONTENT_ENCODING, content_encoding); } // transfer encoding const NPT_String& transfer_encoding = entity->GetTransferEncoding(); if (!transfer_encoding.IsEmpty()) { headers.SetHeader(NPT_HTTP_HEADER_TRANSFER_ENCODING, transfer_encoding); } } else if (!headers.GetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH)) { // force content length to 0 if there is no message body // (necessary for 1.1 or 1.0 with keep-alive connections) headers.SetHeader(NPT_HTTP_HEADER_CONTENT_LENGTH, "0"); } const NPT_String* content_length = headers.GetHeaderValue(NPT_HTTP_HEADER_CONTENT_LENGTH); const NPT_String* transfer_encoding = headers.GetHeaderValue(NPT_HTTP_HEADER_TRANSFER_ENCODING); const NPT_String* connection_header = headers.GetHeaderValue(NPT_HTTP_HEADER_CONNECTION); if (keep_alive) { if (connection_header && connection_header->Compare("close") == 0) { keep_alive = false; } else { // the request says client supports keep-alive // but override if response has content-length header or // transfer chunked encoding keep_alive = content_length || (transfer_encoding && transfer_encoding->Compare(NPT_HTTP_TRANSFER_ENCODING_CHUNKED) == 0); } } // only write keep-alive header for 1.1 if it's close NPT_String protocol = response->GetProtocol(); if (protocol.Compare(NPT_HTTP_PROTOCOL_1_0, true) == 0 || !keep_alive) { headers.SetHeader(NPT_HTTP_HEADER_CONNECTION, keep_alive?"keep-alive":"close", true); } headers.SetHeader(NPT_HTTP_HEADER_SERVER, PLT_HTTP_DEFAULT_SERVER, false); // set but don't replace PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINE, "PLT_HttpServerSocketTask::Write", response); // create a memory stream to buffer the headers NPT_MemoryStream header_stream; response->Emit(header_stream); // send the headers NPT_CHECK_WARNING(output_stream.WriteFully(header_stream.GetData(), header_stream.GetDataSize())); return NPT_SUCCESS; }