8 #include "http_request.h"
12 struct http_connection_s
{
17 http_request_t
*request
;
19 typedef struct http_connection_s http_connection_t
;
23 httpd_callbacks_t callbacks
;
28 http_connection_t
*connections
;
30 /* These variables only edited mutex locked */
33 thread_handle_t thread
;
34 mutex_handle_t run_mutex
;
36 /* Server fd for accepting connections */
41 httpd_init(logger_t
*logger
, httpd_callbacks_t
*callbacks
, int max_connections
, int use_rtsp
)
47 assert(max_connections
> 0);
49 /* Allocate the httpd_t structure */
50 httpd
= calloc(1, sizeof(httpd_t
));
55 httpd
->use_rtsp
= !!use_rtsp
;
56 httpd
->max_connections
= max_connections
;
57 httpd
->connections
= calloc(max_connections
, sizeof(http_connection_t
));
58 if (!httpd
->connections
) {
63 /* Use the logger provided */
64 httpd
->logger
= logger
;
66 /* Save callback pointers */
67 memcpy(&httpd
->callbacks
, callbacks
, sizeof(httpd_callbacks_t
));
69 /* Initial status joined */
77 httpd_destroy(httpd_t
*httpd
)
82 free(httpd
->connections
);
88 httpd_add_connection(httpd_t
*httpd
, int fd
, unsigned char *local
, int local_len
, unsigned char *remote
, int remote_len
)
92 for (i
=0; i
<httpd
->max_connections
; i
++) {
93 if (!httpd
->connections
[i
].connected
) {
97 if (i
== httpd
->max_connections
) {
98 logger_log(httpd
->logger
, LOGGER_INFO
, "Max connections reached\n");
99 shutdown(fd
, SHUT_RDWR
);
104 httpd
->connections
[i
].socket_fd
= fd
;
105 httpd
->connections
[i
].connected
= 1;
106 httpd
->connections
[i
].user_data
= httpd
->callbacks
.conn_init(httpd
->callbacks
.opaque
, local
, local_len
, remote
, remote_len
);
110 httpd_remove_connection(httpd_t
*httpd
, http_connection_t
*connection
)
112 if (connection
->request
) {
113 http_request_destroy(connection
->request
);
114 connection
->request
= NULL
;
116 httpd
->callbacks
.conn_destroy(connection
->user_data
);
117 shutdown(connection
->socket_fd
, SHUT_WR
);
118 closesocket(connection
->socket_fd
);
119 connection
->connected
= 0;
123 httpd_thread(void *arg
)
125 httpd_t
*httpd
= arg
;
137 MUTEX_LOCK(httpd
->run_mutex
);
138 if (!httpd
->running
) {
139 MUTEX_UNLOCK(httpd
->run_mutex
);
142 MUTEX_UNLOCK(httpd
->run_mutex
);
144 /* Set timeout value to 5ms */
148 /* Get the correct nfds value and set rfds */
150 FD_SET(httpd
->server_fd
, &rfds
);
151 nfds
= httpd
->server_fd
+1;
152 for (i
=0; i
<httpd
->max_connections
; i
++) {
154 if (!httpd
->connections
[i
].connected
) {
157 socket_fd
= httpd
->connections
[i
].socket_fd
;
158 FD_SET(socket_fd
, &rfds
);
159 if (nfds
<= socket_fd
) {
164 ret
= select(nfds
, &rfds
, NULL
, NULL
, &tv
);
166 /* Timeout happened */
168 } else if (ret
== -1) {
169 /* FIXME: Error happened */
170 logger_log(httpd
->logger
, LOGGER_INFO
, "Error in select\n");
174 if (FD_ISSET(httpd
->server_fd
, &rfds
)) {
175 struct sockaddr_storage remote_saddr
;
176 socklen_t remote_saddrlen
;
177 struct sockaddr_storage local_saddr
;
178 socklen_t local_saddrlen
;
179 unsigned char *local
, *remote
;
180 int local_len
, remote_len
;
183 remote_saddrlen
= sizeof(remote_saddr
);
184 fd
= accept(httpd
->server_fd
, (struct sockaddr
*)&remote_saddr
, &remote_saddrlen
);
186 /* FIXME: Error happened */
190 local_saddrlen
= sizeof(local_saddr
);
191 ret
= getsockname(fd
, (struct sockaddr
*)&local_saddr
, &local_saddrlen
);
197 logger_log(httpd
->logger
, LOGGER_INFO
, "Accepted client on socket %d\n", fd
);
198 local
= netutils_get_address(&local_saddr
, &local_len
);
199 remote
= netutils_get_address(&remote_saddr
, &remote_len
);
201 httpd_add_connection(httpd
, fd
, local
, local_len
, remote
, remote_len
);
203 for (i
=0; i
<httpd
->max_connections
; i
++) {
204 http_connection_t
*connection
= &httpd
->connections
[i
];
206 if (!connection
->connected
) {
209 if (!FD_ISSET(connection
->socket_fd
, &rfds
)) {
213 /* If not in the middle of request, allocate one */
214 if (!connection
->request
) {
215 connection
->request
= http_request_init(httpd
->use_rtsp
);
216 assert(connection
->request
);
219 logger_log(httpd
->logger
, LOGGER_DEBUG
, "Receiving on socket %d\n", httpd
->connections
[i
].socket_fd
);
220 ret
= recv(connection
->socket_fd
, buffer
, sizeof(buffer
), 0);
222 logger_log(httpd
->logger
, LOGGER_INFO
, "Connection closed\n");
223 httpd_remove_connection(httpd
, connection
);
227 /* Parse HTTP request from data read from connection */
228 http_request_add_data(connection
->request
, buffer
, ret
);
229 if (http_request_has_error(connection
->request
)) {
230 logger_log(httpd
->logger
, LOGGER_INFO
, "Error in parsing: %s\n", http_request_get_error_name(connection
->request
));
231 httpd_remove_connection(httpd
, connection
);
235 /* If request is finished, process and deallocate */
236 if (http_request_is_complete(connection
->request
)) {
237 http_response_t
*response
= NULL
;
239 httpd
->callbacks
.conn_request(connection
->user_data
, connection
->request
, &response
);
240 http_request_destroy(connection
->request
);
241 connection
->request
= NULL
;
249 /* Get response data and datalen */
250 data
= http_response_get_data(response
, &datalen
);
253 while (written
< datalen
) {
254 ret
= send(connection
->socket_fd
, data
+written
, datalen
-written
, 0);
256 /* FIXME: Error happened */
257 logger_log(httpd
->logger
, LOGGER_INFO
, "Error in sending data\n");
263 logger_log(httpd
->logger
, LOGGER_INFO
, "Didn't get response\n");
265 http_response_destroy(response
);
270 /* Remove all connections that are still connected */
271 for (i
=0; i
<httpd
->max_connections
; i
++) {
272 http_connection_t
*connection
= &httpd
->connections
[i
];
274 if (!connection
->connected
) {
277 logger_log(httpd
->logger
, LOGGER_INFO
, "Removing connection\n");
278 httpd_remove_connection(httpd
, connection
);
281 logger_log(httpd
->logger
, LOGGER_INFO
, "Exiting thread\n");
287 httpd_start(httpd_t
*httpd
, unsigned short *port
)
292 MUTEX_LOCK(httpd
->run_mutex
);
293 if (httpd
->running
|| !httpd
->joined
) {
294 MUTEX_UNLOCK(httpd
->run_mutex
);
298 httpd
->server_fd
= netutils_init_socket(port
, 1, 0);
299 if (httpd
->server_fd
== -1) {
300 logger_log(httpd
->logger
, LOGGER_INFO
, "Error initialising socket %d\n", SOCKET_GET_ERROR());
301 MUTEX_UNLOCK(httpd
->run_mutex
);
304 if (listen(httpd
->server_fd
, 5) == -1) {
305 logger_log(httpd
->logger
, LOGGER_INFO
, "Error listening to socket\n");
306 MUTEX_UNLOCK(httpd
->run_mutex
);
309 logger_log(httpd
->logger
, LOGGER_INFO
, "Initialized server socket\n");
311 /* Set values correctly and create new thread */
314 THREAD_CREATE(httpd
->thread
, httpd_thread
, httpd
);
315 MUTEX_UNLOCK(httpd
->run_mutex
);
321 httpd_stop(httpd_t
*httpd
)
325 MUTEX_LOCK(httpd
->run_mutex
);
326 if (!httpd
->running
|| httpd
->joined
) {
327 MUTEX_UNLOCK(httpd
->run_mutex
);
331 MUTEX_UNLOCK(httpd
->run_mutex
);
333 THREAD_JOIN(httpd
->thread
);
335 MUTEX_LOCK(httpd
->run_mutex
);
337 MUTEX_UNLOCK(httpd
->run_mutex
);