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 }