Delete helloworld.c
[matches/MCTX3420.git] / testing / web2io / webserver.c
1 /**
2  * @file webserver.c
3  * @purpose Test implementing a minimalistic webserver
4  * 
5  */
6
7 #define _POSIX_C_SOURCE 200809L // needed for some POSIX stuff to work
8
9 // --- Standard headers --- //
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h> // string helper functions
13 #include <ctype.h> // character types
14 #include <signal.h> // for signal handling
15
16 // --- Custom headers --- //
17 #include "log.h" // C functions to handle logging
18 #include "options.h" // Options structure
19 #include "network.h" // C functions to handle low level networking
20
21 // --- Variable definitions --- //
22 Options options; // declared in "options.h"
23
24 // --- Function definitions --- //
25
26 /**
27  * @funct ParseArguments
28  * @purpose Parse Arguments and setup the global options variable
29  * @param argc - Num args
30  * @param argv - Array of args
31  */
32 void ParseArguments(int argc, char ** argv)
33 {
34         options.program = argv[0];
35         options.verbosity = LOGDEBUG;
36         if (argc > 1)
37                 options.port = atoi(argv[1]); // Allow us change the port for testing (I keep getting "address in use" errors)
38         else
39                 options.port = 8080; // Using 8080 instead of 80 for now because to use 80 you have to run the program as root
40         options.sfd = -1;
41         options.bound_sfd = -1;
42         log_print(LOGDEBUG, "ParseArguments", "Called as %s with %d arguments.", options.program, argc);
43 }
44
45 /**
46  * @funct SignalHandler
47  * @purpose Handle signals
48  * @param sig - The signal
49  */
50 void SignalHandler(int sig)
51 {
52         // At the moment just always exit.
53         // Call `exit` so that Cleanup will be called to... clean up.
54         log_print(LOGWARN, "SignalHandler", "Got signal %d (%s). Exiting.", sig, strsignal(sig));
55         exit(sig);
56 }
57
58 /**
59  * @funct Cleanup
60  * @purpose Called when program exits
61  */
62 void Cleanup()
63 {
64         log_print(LOGDEBUG, "Cleanup", "Begin cleanup.");
65         if (options.sfd >= 0)
66         {
67                 Network_close(options.sfd); // close socket
68                 options.sfd = -1;
69         }
70         if (options.bound_sfd >= 0)
71         {
72                 Network_close(options.bound_sfd); // unbind
73                 options.bound_sfd = -1;
74         }
75         log_print(LOGDEBUG, "Cleanup", "Unbound from port %d successfully", options.port);
76         log_print(LOGDEBUG, "Cleanup", "Done.");
77
78 }
79
80
81 /**
82  * @funct Get
83  * @purpose Respond to a GET request
84  * @param request - The request string
85  * @param sfd - Socket to respond through
86  */
87 void Get(char * request, int sfd)
88 {
89         log_print(LOGDEBUG, "Get", "Got GET request: \"%s\"", request);
90
91         int i = 0;
92         while (!isspace(request[++i]) && request[i] != '\0'); //NOTE: Don't need to check first character
93         request[i] = '\0';
94
95         char response[BUFSIZ];
96         // TODO: Magical low level interfacing stuff!
97
98         int len = sprintf(response, "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
99         write(sfd, response, len);
100         len = 0;
101
102         if (strcmp("/sensor", request) == 0) // dummy test
103         {
104                 len = sprintf(response, "SENSOR OFFLINE\n");
105         }
106         else
107         {
108                 FILE * f = fopen(request+1, "r");
109                 if (f == NULL)
110                 {
111                         log_print(LOGWARN, "Get", "File \"%s\" doesn't exist", request+1);
112                         len = sprintf(response, "You requested \"%s\" using GET\n", request);
113                 }
114                 else
115                 {
116                         while (fgets(response, sizeof(response), f) != NULL)
117                         {
118                                 write(sfd, response, strlen(response));
119                         }
120                 }
121         }
122         if (len > 0)
123                 write(sfd, response, len);
124 }
125
126 /**
127  * @funct Post
128  * @purpose Respond to a POST request
129  * @param request - The request string
130  * @param sfd - Socket to respond through
131  */
132 void Post(char * request, int sfd)
133 {
134         log_print(LOGDEBUG, "Post", "Got POST request: \"%s\"", request);
135         int i = 0;
136         while (!isspace(request[++i]) && request[i] != '\0'); //NOTE: Don't need to check first character
137         request[i] = '\0';
138
139         char response[BUFSIZ];
140         int len = sprintf(response, "HTTP/1.1 200 OK\nContent-type: text/html\n\n");
141         write(sfd, response, len);
142         len = 0;
143
144         // TODO: Magical low level interfacing stuff!
145
146
147         if (strcmp("/actuator", request) == 0) // dummy test
148         {
149                 len = sprintf(response, "ACTUATOR OFFLINE\n");
150         }
151         else
152         {       
153                 len = sprintf(response, "You requested \"%s\" using POST\n", request);
154         }
155         if (len > 0)
156                 write(sfd, response, len);
157
158 }
159
160 /**
161  * @funct main
162  * @purpose Main program
163  * @param argc - Num arguments
164  * @param argv - Argument string array
165  * @returns error code (0 for no error)
166  */
167
168 int main(int argc, char ** argv)
169 {
170         // Parse Arguments
171         ParseArguments(argc, argv);
172         // Set Cleanup to be called on program exit
173         atexit(Cleanup);
174
175         // Setup signal handlers
176         int signals_to_handle[] = {SIGTERM, SIGINT, SIGHUP, SIGPIPE};
177         for (int i = 0; i < sizeof(signals_to_handle)/sizeof(int); ++i)
178         {
179                 int s = signals_to_handle[i];
180                 if (signal(s, SignalHandler) == SIG_ERR)
181                         error("main", "Setting signal handler for %d (%s): %s", s, strsignal(s), strerror(errno));
182         }
183
184         // Bind to the port
185         options.bound_sfd = Network_server_bind(options.port, &(options.port));
186         log_print(LOGDEBUG, "main", "Bound to port %d succesfully", options.port);
187
188         while (true)
189         {
190                         // Listen for a client
191                         options.sfd = Network_server_listen(options.bound_sfd, NULL);
192                         
193                         log_print(LOGDEBUG, "main", "Connected to client");
194         
195                         char buffer[BUFSIZ]; //NOTE: Won't be able to respond to requests longer than BUFSIZ
196                         // read a request
197                         int len = read(options.sfd, buffer, sizeof(buffer));
198                         log_print(LOGDEBUG, "main", "Read %d characters. Buffer is \"%s\"", len, buffer);
199
200                         // Parse request
201                         for (int i = 0; i < sizeof(buffer) && buffer[i] != '\0'; ++i)
202                         {
203                                 // Look for "GET" or "POST" followed by a whitespace
204                                 if (isspace(buffer[i])) // whitespace
205                                 {
206                                         while (isspace(buffer[++i]) && buffer[i] != '\0'); // Skip whitespace
207                                         char * req = buffer+i; // set request string
208                                 
209                                         buffer[i-1] = '\0'; // terminate request type
210                                         while (buffer[++i] != '\n' && buffer[i] != '\0'); // find next newline
211                                         buffer[i-1] = '\0';
212                                         
213                                         if (strcmp("GET", buffer) == 0) // Compare with "GET"
214                                         {
215                                                 Get(req, options.sfd);
216                                         }
217                                         else if (strcmp("POST", buffer) == 0) // Compare with "POST"
218                                         {
219                                                 Post(req, options.sfd);
220                                         }
221                                         else // Unknown request
222                                         {
223                                                 log_print(LOGWARN, "main", "Unrecognised request type \"%s\" (request \"%s\")", buffer, req);
224                                                 char response[] = "Error: Unrecognised request\n";
225                                                 write(options.sfd, response, sizeof(response));
226                                         }
227                                         break;
228                                 }
229                         }
230
231                         // Close connection
232                         Network_close(options.sfd);
233                         options.sfd = -1;
234                         log_print(LOGDEBUG, "main", "Closed connection to client");
235         }
236
237
238         
239         
240         return 0;
241 }
242
243

UCC git Repository :: git.ucc.asn.au