#ifndef HV_HTTP_RESPONSE_WRITER_H_ #define HV_HTTP_RESPONSE_WRITER_H_ #include "Channel.h" #include "HttpMessage.h" namespace hv { class HttpResponseWriter : public SocketChannel { public: HttpResponsePtr response; enum State { SEND_BEGIN, SEND_HEADER, SEND_BODY, SEND_CHUNKED, SEND_CHUNKED_END, SEND_END, } state; HttpResponseWriter(hio_t* io, const HttpResponsePtr& resp) : SocketChannel(io) , response(resp) , state(SEND_BEGIN) {} ~HttpResponseWriter() {} // Begin -> End // Begin -> WriteResponse -> End // Begin -> WriteStatus -> WriteHeader -> WriteBody -> End // Begin -> EndHeaders("Content-Type", "text/event-stream") -> write -> write -> ... -> close // Begin -> EndHeaders("Content-Length", content_length) -> WriteBody -> WriteBody -> ... -> End // Begin -> EndHeaders("Transfer-Encoding", "chunked") -> WriteChunked -> WriteChunked -> ... -> End int Begin() { state = SEND_BEGIN; return 0; } int WriteStatus(http_status status_codes) { response->status_code = status_codes; return 0; } int WriteHeader(const char* key, const char* value) { response->headers[key] = value; return 0; } template int WriteHeader(const char* key, T num) { response->headers[key] = hv::to_string(num); return 0; } int EndHeaders(const char* key = NULL, const char* value = NULL) { if (state != SEND_BEGIN) return -1; if (key && value) { response->headers[key] = value; } std::string headers = response->Dump(true, false); state = SEND_HEADER; return write(headers); } template int EndHeaders(const char* key, T num) { std::string value = hv::to_string(num); return EndHeaders(key, value.c_str()); } int WriteChunked(const char* buf, int len = -1) { int ret = 0; if (len == -1) len = strlen(buf); if (state == SEND_BEGIN) { EndHeaders("Transfer-Encoding", "chunked"); } char chunked_header[64]; int chunked_header_len = snprintf(chunked_header, sizeof(chunked_header), "%x\r\n", len); write(chunked_header, chunked_header_len); if (buf && len) { ret = write(buf, len); state = SEND_CHUNKED; } else { state = SEND_CHUNKED_END; } write("\r\n", 2); return ret; } int WriteChunked(const std::string& str) { return WriteChunked(str.c_str(), str.size()); } int EndChunked() { return WriteChunked(NULL, 0); } int WriteBody(const char* buf, int len = -1) { if (response->IsChunked()) { return WriteChunked(buf, len); } if (len == -1) len = strlen(buf); if (state == SEND_BEGIN) { response->body.append(buf, len); return len; } else { state = SEND_BODY; return write(buf, len); } } int WriteBody(const std::string& str) { return WriteBody(str.c_str(), str.size()); } int WriteResponse(HttpResponse* resp) { if (resp == NULL) { response->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR; return 0; } bool is_dump_headers = state == SEND_BEGIN ? true : false; std::string msg = resp->Dump(is_dump_headers, true); state = SEND_BODY; return write(msg); } int End(const char* buf = NULL, int len = -1) { if (state == SEND_END) return 0; if (!isConnected()) { state = SEND_END; return -1; } int ret = 0; if (state == SEND_CHUNKED) { if (buf) { ret = WriteChunked(buf, len); } if (state == SEND_CHUNKED) { EndChunked(); } } else { if (buf) { ret = WriteBody(buf, len); } bool is_dump_headers = true; bool is_dump_body = true; if (state == SEND_HEADER) { is_dump_headers = false; } else if (state == SEND_BODY) { is_dump_headers = false; is_dump_body = false; } if (is_dump_body) { std::string msg = response->Dump(is_dump_headers, is_dump_body); ret = write(msg); } } state = SEND_END; if (!response->IsKeepAlive()) { close(true); } return ret; } int End(const std::string& str) { return End(str.c_str(), str.size()); } }; } typedef std::shared_ptr HttpResponseWriterPtr; #endif // HV_HTTP_RESPONSE_WRITER_H_