Investigate software for interfacing with hardware via HTTP requests
[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 low level 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 <wctype.h> // wide character classication (white space)
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         options.port = 8080; // Using 8080 instead of 80 for now because to use 80 you have to run the program as root
37         log_print(LOGDEBUG, "ParseArguments", "Called as %s with %d arguments.", options.program, argc);
38 }
39
40 /**
41  * @funct SignalHandler
42  * @purpose Handle signals
43  * @param sig - The signal
44  */
45 void SignalHandler(int sig)
46 {
47         // At the moment just always exit.
48         // Call `exit` so that Cleanup will be called to... clean up.
49         log_print(LOGWARN, "SignalHandler", "Got signal %d (%s). Exiting.", sig, strsignal(sig));
50         exit(sig);
51 }
52
53 /**
54  * @funct Cleanup
55  * @purpose Called when program exits
56  */
57 void Cleanup()
58 {
59         log_print(LOGDEBUG, "Cleanup", "Begin cleanup.");
60         Network_close(options.sfd); // close socket
61         Network_close(options.bound_sfd); // unbind
62         log_print(LOGDEBUG, "Cleanup", "Unbound from port %d successfully", options.port);
63         log_print(LOGDEBUG, "Cleanup", "Done.");
64
65 }
66
67
68 /**
69  * @funct Get
70  * @purpose Respond to a GET request
71  * @param request - The request string
72  * @param sfd - Socket to respond through
73  */
74 void Get(const char * request, int sfd)
75 {
76         log_print(LOGDEBUG, "Get", "Got GET request: %s", request);
77         char response[BUFSIZ];
78
79         // TODO: Magical low level interfacing stuff!
80
81         int len = sprintf(response, "Content-type: text/plain\n\nYou requested %s using GET\n", request);
82         write(sfd, response, len);
83 }
84
85 /**
86  * @funct Post
87  * @purpose Respond to a POST request
88  * @param request - The request string
89  * @param sfd - Socket to respond through
90  */
91 void Post(const char * request, int sfd)
92 {
93         log_print(LOGDEBUG, "Post", "Got POST request: %s", request);
94         char response[BUFSIZ];
95
96         // TODO: Magical low level interfacing stuff!
97
98         int len = sprintf(response, "Content-type: text/plain\n\nYou requested %s using POST\n", request);
99         write(sfd, response, len);
100
101 }
102
103 /**
104  * @funct main
105  * @purpose Main program
106  * @param argc - Num arguments
107  * @param argv - Argument string array
108  * @returns error code (0 for no error)
109  */
110
111 int main(int argc, char ** argv)
112 {
113         // Parse Arguments
114         ParseArguments(argc, argv);
115         // Set Cleanup to be called on program exit
116         atexit(Cleanup);
117
118         // Setup signal handlers
119         int signals_to_handle[] = {SIGTERM, SIGINT, SIGHUP, SIGPIPE};
120         for (int i = 0; i < sizeof(signals_to_handle)/sizeof(int); ++i)
121         {
122                 int s = signals_to_handle[i];
123                 if (signal(s, SignalHandler) == SIG_ERR)
124                         error("main", "Setting signal handler for %d (%s): %s", s, strsignal(s), strerror(errno));
125         }
126
127         // Bind to the port
128         options.bound_sfd = Network_server_bind(options.port, &(options.port));
129         log_print(LOGDEBUG, "main", "Bound to port %d succesfully", options.port);
130
131         while (true)
132         {
133                         // Listen for a client
134                         options.sfd = Network_server_listen(options.bound_sfd, NULL);
135                         
136                         log_print(LOGDEBUG, "main", "Connected to client");
137         
138                         char buffer[BUFSIZ]; //NOTE: Won't be able to respond to requests longer than BUFSIZ
139                         // read a request
140                         int len = read(options.sfd, buffer, sizeof(buffer));
141                         log_print(LOGDEBUG, "main", "Read %d characters. Buffer is %s", len, buffer);
142
143                         // Parse request
144                         for (int i = 0; i < sizeof(buffer) && buffer[i] != '\0'; ++i)
145                         {
146                                 // Look for "GET" or "POST" followed by a whitespace
147                                 if (iswspace(buffer[i])) // whitespace
148                                 {
149                                         while (iswspace(buffer[++i]) && buffer[i] != '\0'); // Skip whitespace
150                                         char * req = buffer+i; // set request string
151                                         buffer[i] = '\0'; // terminate request type
152                                         if (strcmp("GET", buffer) == 0) // Compare with "GET"
153                                         {
154                                                 Get(req, options.sfd);
155                                         }
156                                         else if (strcmp("POST", buffer) == 0) // Compare with "POST"
157                                         {
158                                                 Post(req, options.sfd);
159                                         }
160                                         else // Unknown request
161                                         {
162                                                 log_print(LOGWARN, "main", "Unrecognised request type %s (request %s)", buffer, req);
163                                                 char response[] = "Content-type: text/plain\n\nError: Unrecognised request";
164                                                 write(options.sfd, response, sizeof(response));
165                                         }
166                                         break;
167                                 }
168                         }
169
170                         // Close connection
171                         Network_close(options.sfd);
172                         log_print(LOGDEBUG, "main", "Closed connection to client");
173         }
174
175
176         
177         
178         return 0;
179 }
180
181

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