1 /** 2 * Contains HTTP response components. 3 */ 4 module handy_httpd.response; 5 6 import std.array; 7 import std.string : format, representation; 8 import std.conv; 9 import std.socket : Socket; 10 11 /** 12 * The data that the HTTP server will send back to clients. 13 */ 14 struct HttpResponse { 15 /** 16 * The status code. 17 */ 18 public ushort status; 19 20 /** 21 * A short textual representation of the status. 22 */ 23 public string statusText; 24 25 /** 26 * An associative array of headers. 27 */ 28 public string[string] headers; 29 30 /** 31 * The socket that's used to send data to the client. 32 */ 33 public Socket clientSocket; 34 35 private bool flushed = false; 36 37 /** 38 * Sets the status of the response. 39 * Params: 40 * status = The status code. 41 * Returns: The response object, for method chaining. 42 */ 43 public HttpResponse setStatus(ushort status) { 44 this.status = status; 45 return this; 46 } 47 48 /** 49 * Sets the status text of the response. 50 * Params: 51 * statusText = The status text. 52 * Returns: The response object, for method chaining. 53 */ 54 public HttpResponse setStatusText(string statusText) { 55 this.statusText = statusText; 56 return this; 57 } 58 59 /** 60 * Adds a header to the response. 61 * Params: 62 * name = The name of the header. 63 * value = The value to set for the header. 64 * Returns: The response object, for method chaining. 65 */ 66 public HttpResponse addHeader(string name, string value) { 67 this.headers[name] = value; 68 return this; 69 } 70 71 public void flushHeaders() { 72 if (flushed) return; 73 auto app = appender!string; 74 app ~= format!"HTTP/1.1 %d %s\r\n"(this.status, this.statusText); 75 foreach (name, value; this.headers) { 76 app ~= format!"%s: %s\r\n"(name, value); 77 } 78 app ~= "\r\n"; 79 ubyte[] data = cast(ubyte[]) app[]; 80 auto sent = this.clientSocket.send(data); 81 if (sent == Socket.ERROR) throw new Exception("Socket error occurred while writing status and headers."); 82 } 83 84 /** 85 * Writes the given string content to the body of the response. If this 86 * response has not yet written its status line and headers, it will do 87 * that first. 88 * Params: 89 * body = The content to write. 90 */ 91 public void writeBody(string body) { 92 if (!flushed) addHeader("Content-Length", body.length.to!string); 93 flushHeaders(); 94 auto sent = this.clientSocket.send(cast(ubyte[]) body); 95 if (sent == Socket.ERROR) throw new Exception("Socket error occurred while writing body."); 96 } 97 }