summaryrefslogtreecommitdiffhomepage
path: root/response.c
blob: b2a72d76124c84bbc973ae252887d7b2cf20f426 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "response.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <lua.h>

#include "http.h"

/// Sends a response based on the current top value on the stack.
/// \param L Lua state to send response for
/// \param fd File descriptor of a socket to write to
/// \return Bytes written or -1 on error
/// \see write(2)
// TODO: Consider splitting response_send into smaller functions.
int response_send(lua_State * L, const int fd)
{
	static const char * error_response =
		"HTTP/1.1 500\r\n"
		"Connection: close\r\n"
		"\r\n";

	if (0 == lua_istable(L, -1) || 0 == lua_checkstack(L, 5))
	{
		lua_pop(L, 1);
		return write(fd, error_response, strlen(error_response));
	}

	int bytes_total = 2048 * sizeof(char);
	int bytes_used = 0;
	char * buffer = malloc(bytes_total);

	lua_pushstring(L, "status");
	lua_gettable(L, -2);
	const lua_Integer status = lua_tointeger(L, -1);
	lua_pop(L, 1);

	if (0 == status || NULL == buffer)
	{
		lua_pop(L, 1);
		return write(fd, error_response, strlen(error_response));
	}

	bytes_used = snprintf(buffer, bytes_total, "HTTP/1.1 %d\r\n\r\n", (int) status) - 2;

	lua_pushstring(L, "headers");
	lua_gettable(L, -2);

	if (0 == lua_istable(L, -1))
	{
		lua_pop(L, 2);
		return write(fd, buffer, bytes_used + 2);
	}

	static const char * header_pattern = "%s: %s\r\n";

	lua_pushnil(L);
	while (0 != lua_next(L, -2) && 0 != lua_isstring(L, -2))
	{
		const char * key = lua_tostring(L, -2);
		const char * value = lua_tostring(L, -1); // TODO: Check the type of the header's value?
		const int bytes_left = bytes_total - bytes_used;
		int new_bytes = snprintf(buffer + bytes_used, bytes_left, header_pattern, key, value);
		while (bytes_left <= new_bytes)
		{
			bytes_total += 2048;
			if (65536 < bytes_total)
			{
				free(buffer);
				lua_pop(L, 4);
				return write(fd, error_response, strlen(error_response));
			}
			buffer = realloc(buffer, bytes_total);
			if (NULL == buffer)
			{
				lua_pop(L, 4);
				return write(fd, error_response, strlen(error_response));
			}
			new_bytes = snprintf(buffer + bytes_used, bytes_left, header_pattern, key, value);
		}
		bytes_used += new_bytes;
		lua_pop(L, 1);
	}

	if (bytes_total < bytes_used + 2)
	{
		bytes_total += 2;
		buffer = realloc(buffer, bytes_total);
		if (NULL == buffer)
		{
			lua_pop(L, 2);
			return write(fd, error_response, strlen(error_response));
		}
	}

	lua_pop(L, 1);
	buffer[bytes_used] = '\r';
	buffer[bytes_used + 1] = '\n';
	bytes_used += 2;

	// TODO: Add data write

	int result = write(fd, buffer, bytes_used );
	free(buffer);
	lua_pop(L, 1);

	return result;
}

/// Sends a simple response only with a status to the client.
/// \param fd File descriptor of the client socket
/// \param status HTTP response status code
/// \return Negative value if an error was encountered; numbers of bytes written otherwise
int respond_only_status(const int fd, const enum status status)
{
	static const char * pattern =
		"HTTP/1.1 %s\r\n"
		"Connection: close\r\n"
		"\r\n";

	return dprintf(fd, pattern, status_str[status]);
}

/// Sends a response with a status and a body to the client.
/// \param fd File descriptor of the client socket
/// \param status HTTP response status code
/// \param body Content that will be sent
/// \param size Size of the content in bytes
/// \return Negative value if an error was encountered; numbers of bytes written otherwise
int respond_with_body(const int fd, const enum status status, const char * body, const int size)
{
	static const char * pattern =
		"HTTP/1.1 %s\r\n"
		"Connection: close\r\n"
		"Content-Type: application/json\r\n"
		"Content-Size: %d\r\n"
		"\r\n";

	if (0 > dprintf(fd, pattern, status_str[status], size))
	{
		return -1; // TODO: Handle errors properly
	}

	return write(fd, body, size);
}