greenhouse
http_response_builder.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "TCPSocket.h"
4 #include "http_parsed_url.h"
5 #include "http_parser.h"
6 #include <cstdint>
7 #include <cstdlib>
8 #include <errno.h>
9 #include <map>
10 #include <string>
11 
12 using namespace std;
13 
14 static const char *get_http_status_string(uint16_t status_code) {
15  switch (status_code) {
16  case 100:
17  return "Continue";
18  case 101:
19  return "Switching Protocols";
20  case 102:
21  return "Processing";
22  case 200:
23  return "OK";
24  case 201:
25  return "Created";
26  case 202:
27  return "Accepted";
28  case 203:
29  return "Non-Authoritative Information";
30  case 204:
31  return "No Content";
32  case 205:
33  return "Reset Content";
34  case 206:
35  return "Partial Content";
36  case 207:
37  return "Multi-Status";
38  case 208:
39  return "Already Reported";
40  case 226:
41  return "IM Used";
42  case 300:
43  return "Multiple Choices";
44  case 301:
45  return "Moved Permanently";
46  case 302:
47  return "Found";
48  case 303:
49  return "See Other";
50  case 304:
51  return "Not Modified";
52  case 305:
53  return "Use Proxy";
54  case 307:
55  return "Temporary Redirect";
56  case 308:
57  return "Permanent Redirect";
58  case 400:
59  return "Bad Request";
60  case 401:
61  return "Unauthorized";
62  case 402:
63  return "Payment Required";
64  case 403:
65  return "Forbidden";
66  case 404:
67  return "Not Found";
68  case 405:
69  return "Method Not Allowed";
70  case 406:
71  return "Not Acceptable";
72  case 407:
73  return "Proxy Authentication Required";
74  case 408:
75  return "Request Timeout";
76  case 409:
77  return "Conflict";
78  case 410:
79  return "Gone";
80  case 411:
81  return "Length Required";
82  case 412:
83  return "Precondition Failed";
84  case 413:
85  return "Payload Too Large";
86  case 414:
87  return "URI Too Long";
88  case 415:
89  return "Unsupported Media Type";
90  case 416:
91  return "Range Not Satisfiable";
92  case 417:
93  return "Expectation Failed";
94  case 421:
95  return "Misdirected Request";
96  case 422:
97  return "Unprocessable Entity";
98  case 423:
99  return "Locked";
100  case 424:
101  return "Failed Dependency";
102  case 426:
103  return "Upgrade Required";
104  case 428:
105  return "Precondition Required";
106  case 429:
107  return "Too Many Requests";
108  case 431:
109  return "Request Header Fields Too Large";
110  case 451:
111  return "Unavailable For Legal Reasons";
112  case 500:
113  return "Internal Server Error";
114  case 501:
115  return "Not Implemented";
116  case 502:
117  return "Bad Gateway";
118  case 503:
119  return "Service Unavailable";
120  case 504:
121  return "Gateway Timeout";
122  case 505:
123  return "HTTP Version Not Supported";
124  case 506:
125  return "Variant Also Negotiates";
126  case 507:
127  return "Insufficient Storage";
128  case 508:
129  return "Loop Detected";
130  case 510:
131  return "Not Extended";
132  case 511:
133  return "Network Authentication Required";
134  default:
135  return "Unknown";
136  }
137 }
138 
140 public:
141  HttpResponseBuilder(uint16_t a_status_code)
142  : status_code(a_status_code),
143  status_message(get_http_status_string(a_status_code)) {}
144 
149  void set_header(string key, string value) {
150  map<string, string>::iterator it = headers.find(key);
151 
152  if (it != headers.end()) {
153  it->second = value;
154  } else {
155  headers.insert(headers.end(), pair<string, string>(key, value));
156  }
157  }
158 
159  char *build(const void *body, size_t body_size, size_t *size) {
160  char buffer[10];
161  snprintf(buffer, sizeof(buffer), "%d", body_size);
162  set_header("Content-Length", string(buffer));
163 
164  char status_code_buffer[5];
165  snprintf(status_code_buffer, sizeof(status_code_buffer), "%d",
166  status_code /* max 5 digits */);
167 
168  *size = 0;
169 
170  // first line is HTTP/1.1 200 OK\r\n
171  *size +=
172  8 + 1 + strlen(status_code_buffer) + 1 + strlen(status_message) + 2;
173 
174  // after that we'll do the headers
175  typedef map<string, string>::iterator it_type;
176  for (it_type it = headers.begin(); it != headers.end(); it++) {
177  // line is KEY: VALUE\r\n
178  *size += it->first.length() + 1 + 1 + it->second.length() + 2;
179  }
180 
181  // then the body, first an extra newline
182  *size += 2;
183 
184  // body
185  *size += body_size;
186 
187  // Now let's print it
188  char *res = (char *)calloc(*size + 1, 1);
189  char *originalRes = res;
190 
191  res +=
192  sprintf(res, "HTTP/1.1 %s %s\r\n", status_code_buffer, status_message);
193 
194  typedef map<string, string>::iterator it_type;
195  for (it_type it = headers.begin(); it != headers.end(); it++) {
196  // line is KEY: VALUE\r\n
197  res += sprintf(res, "%s: %s\r\n", it->first.c_str(), it->second.c_str());
198  }
199 
200  res += sprintf(res, "\r\n");
201 
202  if (body_size > 0) {
203  memcpy(res, body, body_size);
204  }
205  res += body_size;
206 
207 #ifdef DEBUG
208  printf("\nhttp_response_builder#build\n");
209  printf("----- BEGIN RESPONSE -----\n");
210  printf("%s", originalRes);
211  printf("----- END RESPONSE -----\n");
212 #endif
213 
214  return originalRes;
215  }
216 
217  nsapi_error_t send(TCPSocket *socket, const void *body, size_t body_size) {
218  if (!socket)
219  return NSAPI_ERROR_NO_SOCKET;
220 
221  size_t res_size;
222  char *response = build(body, body_size, &res_size);
223 
224  nsapi_error_t r = socket->send(response, res_size);
225 
226  free(response);
227 
228  return r;
229  }
230 
231 private:
232  uint16_t status_code;
233  const char *status_message;
234  map<string, string> headers;
235 };
nsapi_error_t send(TCPSocket *socket, const void *body, size_t body_size)
void set_header(string key, string value)
map< string, string > headers
char * build(const void *body, size_t body_size, size_t *size)
HttpResponseBuilder(uint16_t a_status_code)
static const char * get_http_status_string(uint16_t status_code)