├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── helloworld.c ├── scgilib.c └── scgilib.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | helloworld 3 | helloworld.dSYM 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sam Alexander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo Note: The SCGI C Library is designed to not require system-wide 3 | @echo installation, instead, you can simply put scgilib.c and scgilib.h 4 | @echo in your project directly. This makefile is only for making the 5 | @echo helloworld.c test program. 6 | @echo 7 | gcc -Wall -Wextra -pedantic -g scgilib.c helloworld.c -o helloworld 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The library consists of just two files: scgilib.h and helloworld.c, which implements a bare-bones "Hello World" server using the library. 2 | 3 | # Features 4 | 5 | Asynchronous, non-blocking sockets. All the necessary socket programming is taken care of in scgilib.c. If one client connects to the library and slooooooowly starts sending a request, and while that request is still trickling in, a second client connects and sends a second request, the library will handle both requests simultaneously, without making the second client wait. This is accomplished without forking the server into multiple processes (thus allowing the server to store an enormous and dynamic database in RAM). 6 | Listening for connections on multiple ports is as easy as calling the library initialization function multiple times. 7 | The library files are generously full of comments, I hope this will facilitate easily modifying the libraries as needed. 8 | 9 | # License 10 | 11 | MIT 12 | 13 | # Documentation 14 | 15 | There are three primary functions for interacting with the library: scgi_initialize for turning the server on, scgi_recv for obtaining connections, and scgi_write for sending the response. Connections are returned by scgi_recv in the form of an scgi_request structure (defined in scgilib.h) which contains fields for common things you might want to know about the request, such as what query string they sent, what their IP address is, etc. 16 | scgi_initialize 17 | 18 | ## int scgi_initialize( int port ); 19 | 20 | Attempt to start an SCGI server which listens for connections to the specified port. 21 | 22 | Returns 1 on success, 0 on failure. 23 | 24 | Can be called multiple times with different port numbers, which will cause the library to listen on each port. (This feature hasn’t been very rigorously tested) 25 | scgi_recv 26 | 27 | ## scgi_request *scgi_recv( void ); 28 | 29 | Returns a pointer to a structure containing data about an incoming request. The structure, struct SCGI_REQUEST, is defined in scgilib.h. If there are multiple connections awaiting a response, scgi_recv will send the one which has been ready the longest. If there are no new connections awaiting response, scgi_recv returns NULL. 30 | 31 | To read the info about the request (e.g., what query string they sent), simply read from the appropriate fields of the structure. See helloworld.c for an example. 32 | 33 | Garbage collection is handled in scgilib.c: the structures returned by scgi_recv are NOT meant to be manually freed. They will automatically be freed shortly after you specify an HTTP response using scgi_write (you ARE sending responses to each request, right? Even if the request is nonsense, you should at least send a 404 File Not Found). A request will also be free’d any time the library detects that the connection has been terminated– this can be dangerous if you still have a pointer to the structure, so see the next paragraph. 34 | 35 | Since you (the library user) do not manually do the garbage collection, you may want to have a way to check whether a given request still exists in memory. For this purpose, you may associate the request with an int, and when/if the SCGI library frees the request, the int will have its value set to 1. This is done by setting the scgi_request’s int *dead field, which is NULL by default. See helloworld.c for an example. 36 | scgi_write 37 | 38 | ## int scgi_write( scgi_request *req, char *txt ); 39 | 40 | Tell the library what HTTP response you would like to be sent in response to the request. This is meant to be called only once per request. Due to the non-blocking sockets feature, the response is not instantly sent, instead it is stored. The actual transmission of the response occurs when scgi_recv is called. If there is no time to send the entire transmission all at once when scgi_recv is called, the library will send as much of the response as it can, and send the rest on subsequent calls to scgi_recv. 41 | 42 | # Example 43 | 44 | For a basic example, see helloworld.c. 45 | 46 | # Instructions 47 | 48 | There is no installation or configuration for the library itself: just act like you wrote the .c and .h files yourself, putting them in the same location as all the other .c files in your project, etc. 49 | 50 | Of course, web browsers don't send requests in SCGI. You must configure your webserver to act as the middleman. I will give instructions in Apache for now (NOTE: these instructions are probably outdated by now). I have not done it in any other webserver; maybe I will add instructions for other webservers later. You can always use a search engine and search for how to use scgi with your webbrowser. 51 | 52 | Instructions for configuring Apache 2.2 to reroute (e.g.) all traffic to /scgilib/helloworld/ to an SCGI server listening on (say) port 8000: 53 | 54 | 1. First, install the mod_scgi module for Apache. 55 | 2. Then add the following to /etc/apache2/apache2.conf (the specific config file may vary with your Apache installation): SCGIMount /scgilib/helloworld/ 127.0.0.1:8000 56 | 3. Finally, reboot Apache. 57 | 4. Having done the above, now any time someone goes to http://www.mywebsite.com/scgilib/helloworld/, Apache takes their request, translates it into SCGI, and forwards it to the SCGI server on port 8000. Assuming that the helloworld server is actually running, it will give a response to Apache, who will then give that to the original client. 58 | 59 | Sorry for the rather poor instructions section. I can only hope that anyone actually looking for something as obscure and specific as an SCGI Library already knows what they’re doing and can figure out the rest on their own! -------------------------------------------------------------------------------- /helloworld.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SCGI C Library 3 | * By Sam Alexander 4 | * 5 | * Version 0.2, Last Updated: 01 Mar 2014 6 | * 7 | * helloworld.c - SCGI Library example file 8 | * Creates a server (on port 8000) to listen for SCGI and respond with Hello World! 9 | * 10 | * To actually make this work, one must configure one's webserver to redirect (certain) traffic to 11 | * port 8000 using the SCGI protocol. How to do this varies from webserver to webserver. 12 | * Of course, 8000 can be replaced with whatever port number you want. 13 | * 14 | * For example, on my Apache server, first I installed the mod-scgi module, then I added 15 | * the following line in etc/apache2/apache2.conf : 16 | * SCGIMount /scgilib/helloworld/ 127.0.0.1:8000 17 | * Then I restarted Apache. 18 | * This tells Apache that when anyone requests /scgilib/helloworld/, Apache is to translate the request 19 | * into the SCGI protocol, forward it to port 8000, obtain a response, and forward the response back to 20 | * whoever made the original request. 21 | * 22 | * Copyright/license: MIT 23 | */ 24 | 25 | #include "scgilib.h" 26 | #include 27 | 28 | #define HELLOWORLDPORT 8000 29 | 30 | /* 31 | * nginx does not correctly implement the SCGI protocol. 32 | * After much hair-pulling, I discovered that as of version 33 | * 1.1.19, nginx will accept SCGI responses if they send 34 | * the header "HTTP/1.1 200 OK" and no other headers. 35 | * 36 | * To make helloworld.c work with Apache, comment out the following line. 37 | * To make it work with nginx, leave the following line defined. 38 | * 39 | */ 40 | 41 | //#define SUPPORT_FOR_BUGGY_NGINX 42 | 43 | 44 | int main(void) 45 | { 46 | int connections; 47 | 48 | /* 49 | * Attempt to initialize the SCGI Library and make it listen on a port 50 | */ 51 | 52 | if ( scgi_initialize( HELLOWORLDPORT ) ) 53 | printf( "Successfully initialized the SCGI library. Listening on port %d.\n", HELLOWORLDPORT ); 54 | else 55 | { 56 | printf( "Could not listen for incoming connections on port %d.\n" 57 | "Aborting helloworld.\n\n", HELLOWORLDPORT ); 58 | return 0; 59 | } 60 | 61 | /* 62 | * Enter an infinite loop, serving up responses to SCGI connections forever. 63 | */ 64 | while ( 1 ) 65 | { 66 | /* 67 | * Check for connections ten times per second (once per 100000 microseconds). 68 | * A typical server (such as this helloworld server) will spend the vast majority of its time sleeping. 69 | * Nothing magical about 100000 microseconds, an SCGI Library user can call the library as often or rarely 70 | * as desired (of course, if you wait foreeeever, eventually your webserver will issue an "Internal Server Error"). 71 | */ 72 | usleep(100000); 73 | 74 | #define MAX_CONNECTIONS_TO_ACCEPT_AT_ONCE 5 75 | 76 | connections = 0; 77 | 78 | while ( connections < MAX_CONNECTIONS_TO_ACCEPT_AT_ONCE ) 79 | { 80 | /* 81 | * If any connections are awaiting the server's attention, scgi_recv() will output a pointer to one of them. 82 | * Otherwise, it will return NULL. 83 | */ 84 | scgi_request *req = scgi_recv(); 85 | int dead; 86 | 87 | if ( req != NULL ) 88 | { 89 | /* 90 | * Got a connection! 91 | */ 92 | connections++; 93 | 94 | /* 95 | * Since there is no way to check whether memory has been free'd, let's give the library a way to 96 | * let us know whether the request still exists in memory or not, by giving it the address of an 97 | * int. Once we've done this, we can check the int at any time, to see whether the request still 98 | * exists in memory. 99 | */ 100 | dead = 0; 101 | req->dead = &dead; 102 | 103 | /* 104 | * Send some log messages to stdout (pretty silly, but this is to illustrate how scgilib works) 105 | */ 106 | 107 | printf( "SCGI C Library received an SCGI connection on port %d.\n", req->descriptor->port->port ); 108 | if ( req->remote_addr ) 109 | printf( "The connection originated from remote IP address %s.\n", req->remote_addr ); 110 | if ( req->http_host ) 111 | printf( "The connection was addressed to domain name %s.\n", req->http_host ); 112 | if ( req->request_method == SCGI_METHOD_GET ) 113 | printf( "The connection made an HTTP GET request.\n" ); 114 | else if ( req->request_method == SCGI_METHOD_POST ) 115 | printf( "The connection made an HTTP POST request.\n" ); 116 | else if ( req->request_method == SCGI_METHOD_HEAD ) 117 | printf( "The connection made an HTTP HEAD request.\n" ); 118 | else 119 | printf( "The connection made some other HTTP request than GET, POST, or HEAD.\n" ); 120 | if ( req->user_agent ) 121 | printf( "The webclient identified itself as: %s\n", req->user_agent ); 122 | if ( req->query_string && *req->query_string ) 123 | printf( "They included a query string: %s\n", req->query_string ); 124 | 125 | #ifndef SUPPORT_FOR_BUGGY_NGINX 126 | if ( !scgi_write( req, "Status: 200 OK\r\n" 127 | "Content-Type: text/plain\r\n\r\n" 128 | "Hello World!" ) ) 129 | #else 130 | if ( !scgi_write( req, "HTTP/1.1 200 OK\r\n\r\n" 131 | "Hello World!" ) ) 132 | #endif 133 | { 134 | printf( "Our response could not be sent, we couldn't allocate the necessary RAM.\n" ); 135 | } 136 | else 137 | if ( dead == 1 ) 138 | printf( "Oh my, something went wrong!\n" 139 | "The connection was killed by the SCGI Library when we tried to send the response.\n" ); 140 | 141 | /* 142 | * From here on, helloworld.c forgets about the request (though the library itself still remembers it) 143 | * so we can relieve the library from having to maintain the req->dead 144 | */ 145 | if ( !dead ) 146 | req->dead = NULL; 147 | printf("\n"); 148 | } 149 | else 150 | break; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /scgilib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SCGI C Library 3 | * By Sam Alexander 4 | * 5 | * Version 0.2, Last Updated: 4 Jun 2012 6 | * 7 | * scgilib.c - SCGI Library code file 8 | * 9 | * Instructions: Compile scgilib.c along with all your other code files. 10 | * #include the accompanying header file "scgilib.h" anywhere you wish 11 | * to use the SCGI Library. 12 | * Use the library's functions to your heart's content. 13 | * 14 | * Copyright/license: MIT 15 | */ 16 | 17 | #include "scgilib.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /* 26 | * Doubly-linked list of ports to listen on 27 | */ 28 | scgi_port *first_scgi_port; 29 | scgi_port *last_scgi_port; 30 | 31 | /* 32 | * Doubly-linked list of requests from clients 33 | */ 34 | scgi_request *first_scgi_req; 35 | scgi_request *last_scgi_req; 36 | 37 | /* 38 | * Doubly-linked list of new requests which have been parsed and are ready to be returned by scgi_recv 39 | */ 40 | scgi_request *first_scgi_unrecved_req; 41 | scgi_request *last_scgi_unrecved_req; 42 | 43 | /* 44 | * Socket programming stuff 45 | */ 46 | fd_set scgi_inset; 47 | fd_set scgi_outset; 48 | fd_set scgi_excset; 49 | 50 | /* 51 | * Function prototypes (there are additional function prototypes in scgilib.h) 52 | */ 53 | int resize_buffer( scgi_desc *d, char **buf ); 54 | void scgi_parse_input( scgi_desc *d ); 55 | void scgi_deal_with_socket_out_of_ram( scgi_desc *d ); 56 | int scgi_is_number( char *arg ); 57 | int scgi_add_header( scgi_desc *d, char *name, char *val ); 58 | 59 | /* 60 | * Listen for incoming requests on all open ports 61 | */ 62 | void scgi_update_connections( void ) 63 | { 64 | scgi_port *p; 65 | 66 | for ( p = first_scgi_port; p; p = p->next ) 67 | scgi_update_connections_port( p ); 68 | } 69 | 70 | /* 71 | * Listen for incoming requests on one specified port 72 | */ 73 | void scgi_update_connections_port( scgi_port *p ) 74 | { 75 | static struct timeval zero_time; 76 | scgi_desc *d, *d_next; 77 | int top_desc; 78 | 79 | /* 80 | * initialize socket stuff 81 | */ 82 | FD_ZERO( &scgi_inset ); 83 | FD_ZERO( &scgi_outset ); 84 | FD_ZERO( &scgi_excset ); 85 | FD_SET( p->sock, &scgi_inset ); 86 | top_desc = p->sock; 87 | 88 | for ( d = p->first_scgi_desc; d; d = d->next ) 89 | { 90 | if ( d->sock > top_desc ) 91 | top_desc = d->sock; 92 | if ( d->state == SCGI_SOCKSTATE_READING_REQUEST ) 93 | FD_SET( d->sock, &scgi_inset ); 94 | else 95 | if ( d->state == SCGI_SOCKSTATE_WRITING_RESPONSE ) 96 | FD_SET( d->sock, &scgi_outset ); 97 | FD_SET( d->sock, &scgi_excset ); 98 | } 99 | 100 | /* 101 | * Poll the sockets! 102 | */ 103 | if ( select( top_desc+1, &scgi_inset, &scgi_outset, &scgi_excset, &zero_time ) < 0 ) 104 | { 105 | scgi_perror( "Fatal: scgilib failed to poll the descriptors." ); 106 | exit(1); 107 | } 108 | 109 | /* 110 | * If we've got a new incoming connection, deal with it 111 | */ 112 | if ( !FD_ISSET( p->sock, &scgi_excset ) && FD_ISSET( p->sock, &scgi_inset ) ) 113 | { 114 | scgi_answer_the_phone(p); 115 | } 116 | 117 | for ( d = p->first_scgi_desc; d; d = d_next ) 118 | { 119 | /* 120 | * We may be killing things in this list as we traverse it, 121 | * so need a safe copy of the next thing in the list. 122 | */ 123 | d_next = d->next; 124 | 125 | d->idle++; 126 | 127 | /* 128 | * Kick connections out if they raise any kind of exception, or if they're idle too long 129 | */ 130 | if ( FD_ISSET( d->sock, &scgi_excset ) 131 | || d->idle > SCGI_KICK_IDLE_AFTER_X_SECS * SCGI_PULSES_PER_SEC ) 132 | { 133 | FD_CLR( d->sock, &scgi_inset ); 134 | FD_CLR( d->sock, &scgi_outset ); 135 | 136 | scgi_kill_socket( d ); 137 | continue; 138 | } 139 | 140 | /* 141 | * Handle remote I/O, provided the connections are ready for it 142 | */ 143 | if ( d->state == SCGI_SOCKSTATE_READING_REQUEST 144 | && FD_ISSET( d->sock, &scgi_inset ) ) 145 | { 146 | d->idle = 0; 147 | scgi_listen_to_request( d ); 148 | } 149 | else 150 | if ( d->state == SCGI_SOCKSTATE_WRITING_RESPONSE 151 | && d->outbuflen > 0 152 | && FD_ISSET( d->sock, &scgi_outset ) ) 153 | { 154 | d->idle = 0; 155 | scgi_flush_response( d ); 156 | } 157 | } 158 | } 159 | 160 | /* 161 | * Kick a connection offline and delete it from memory 162 | */ 163 | void scgi_kill_socket( scgi_desc *d ) 164 | { 165 | SCGI_UNLINK( d, d->port->first_scgi_desc, d->port->last_scgi_desc, next, prev ); 166 | 167 | free( d->buf ); 168 | 169 | free( d->outbuf ); 170 | 171 | free_scgi_request( d->req ); 172 | close( d->sock ); 173 | free( d ); 174 | } 175 | 176 | /* 177 | * Delete an SCGI request from memory 178 | */ 179 | void free_scgi_request( scgi_request *r ) 180 | { 181 | scgi_header *h, *h_next; 182 | scgi_request *ptr; 183 | 184 | if ( !r ) 185 | return; 186 | 187 | /* 188 | * The request is now dead. If the programmer (you) supplied the location of an integer, 189 | * we will use it to signal the request's deadness, so you can avoid trying to do anything 190 | * with the no-longer-existent connection. 191 | */ 192 | if ( r->dead ) 193 | { 194 | *r->dead = 1; 195 | } 196 | 197 | SCGI_UNLINK( r, first_scgi_req, last_scgi_req, next, prev ); 198 | 199 | for ( ptr = first_scgi_unrecved_req; ptr; ptr = ptr->next_unrecved ) 200 | { 201 | if ( ptr == r ) 202 | { 203 | SCGI_UNLINK( r, first_scgi_unrecved_req, last_scgi_unrecved_req, next_unrecved, prev_unrecved ); 204 | break; 205 | } 206 | } 207 | 208 | for ( h = r->first_header; h; h = h_next ) 209 | { 210 | h_next = h->next; 211 | free( h->name ); 212 | free( h->value ); 213 | free( h ); 214 | } 215 | 216 | if ( r->body ) 217 | free( r->body ); 218 | 219 | free( r ); 220 | } 221 | 222 | /* 223 | * Accept new connections 224 | */ 225 | void scgi_answer_the_phone( scgi_port *p ) 226 | { 227 | struct sockaddr_storage their_addr; 228 | socklen_t addr_size = sizeof(their_addr); 229 | int caller; 230 | scgi_desc *d; 231 | scgi_request *req; 232 | 233 | if ( ( caller = accept( p->sock, (struct sockaddr *) &their_addr, &addr_size) ) < 0 ) 234 | { 235 | scgi_perror( "Warning: scgilib's phone rang but something prevented scgilib from answering it." ); 236 | return; 237 | } 238 | 239 | /* 240 | * SCGI is intended for applications which accept multiple connections asynchronously, so 241 | * if a socket cannot be set to non-blocking for some reason, it gets the boot. 242 | */ 243 | if ( ( fcntl( caller, F_SETFL, FNDELAY ) ) == -1 ) 244 | { 245 | scgi_perror( "Warning: scgilib was unable to set a socket to non-blocking mode. scgilib hung up the phone on this socket." ); 246 | close(caller); 247 | return; 248 | } 249 | 250 | /* 251 | * The connection has been made. Let's commit it to RAM. 252 | */ 253 | SCGI_CREATE( d, scgi_desc, 1 ); 254 | d->next = NULL; 255 | d->prev = NULL; 256 | d->port = p; 257 | d->sock = caller; 258 | d->idle = 0; 259 | d->state = SCGI_SOCKSTATE_READING_REQUEST; 260 | d->writehead = NULL; 261 | d->parsed_chars = 0; 262 | d->string_starts = NULL; 263 | d->parser_state = SCGI_PARSE_HEADLENGTH; 264 | 265 | SCGI_CREATE( d->buf, char, SCGI_INITIAL_INBUF_SIZE + 1 ); 266 | d->bufsize = SCGI_INITIAL_INBUF_SIZE; 267 | d->buflen = 0; 268 | *d->buf = '\0'; 269 | 270 | SCGI_CREATE( d->outbuf, char, SCGI_INITIAL_OUTBUF_SIZE + 1 ); 271 | d->outbufsize = SCGI_INITIAL_OUTBUF_SIZE; 272 | d->outbuflen = 0; 273 | *d->outbuf = '\0'; 274 | 275 | SCGI_CREATE( req, scgi_request, 1 ); 276 | req->next = NULL; 277 | req->prev = NULL; 278 | req->next_unrecved = NULL; 279 | req->prev_unrecved = NULL; 280 | req->descriptor = d; 281 | 282 | req->first_header = NULL; 283 | req->last_header = NULL; 284 | req->body = NULL; 285 | req->scgi_content_length = -1; 286 | req->scgi_scgiheader = 0; 287 | req->dead = NULL; 288 | 289 | req->request_method = SCGI_METHOD_UNSPECIFIED; 290 | req->http_host = NULL; 291 | req->query_string = NULL; 292 | req->request_uri = NULL; 293 | req->http_cache_control = NULL; 294 | req->raw_http_cookie = NULL; 295 | req->http_connection = NULL; 296 | req->http_accept_encoding = NULL; 297 | req->http_accept_language = NULL; 298 | req->http_accept_charset = NULL; 299 | req->http_accept = NULL; 300 | req->user_agent = NULL; 301 | req->remote_addr = NULL; 302 | req->server_port = NULL; 303 | req->server_addr = NULL; 304 | req->server_protocol = NULL; 305 | 306 | d->req = req; 307 | 308 | SCGI_LINK( req, first_scgi_req, last_scgi_req, next, prev ); 309 | 310 | SCGI_LINK( d, p->first_scgi_desc, p->last_scgi_desc, next, prev ); 311 | 312 | return; 313 | } 314 | 315 | /* 316 | * If more I/O space is needed than allocated, allocate more (up to a limit) 317 | * Returns 0 (and kills the connection) if the specified limit has been reached 318 | */ 319 | int resize_buffer( scgi_desc *d, char **buf ) 320 | { 321 | int max, *size; 322 | char *tmp; 323 | 324 | if ( *buf == d->buf ) 325 | { 326 | max = SCGI_MAX_INBUF_SIZE; 327 | size = &d->bufsize; 328 | } 329 | else 330 | { 331 | max = SCGI_MAX_OUTBUF_SIZE; 332 | size = &d->outbufsize; 333 | } 334 | 335 | *size *= 2; 336 | if ( *size >= max ) 337 | { 338 | scgi_kill_socket(d); 339 | return 0; 340 | } 341 | 342 | /* 343 | * Special treatment rather than the usual malloc macro, just because I thought this 344 | * particular function might have a bigger risk of sucking up too much RAM and so it 345 | * would be better to handle it directly rather than use a generic macro 346 | */ 347 | tmp = (char *) calloc((*size)+1, sizeof(char) ); 348 | if ( !tmp ) 349 | { 350 | scgi_deal_with_socket_out_of_ram(d); 351 | return 0; 352 | } 353 | 354 | sprintf( tmp, "%s", *buf ); 355 | free( *buf ); 356 | *buf = tmp; 357 | return 1; 358 | } 359 | 360 | /* 361 | * A socket is ready for us to read (continue reading?) its input! So read it. 362 | */ 363 | void scgi_listen_to_request( scgi_desc *d ) 364 | { 365 | int start = d->buflen, readsize; 366 | 367 | /* 368 | * If their buffer is sufficiently near full and there's still more to be read, 369 | * then increase the buffer. If they're spamming with an enormous request, 370 | * the connection will be terminated in resize_buffer. 371 | */ 372 | if ( start >= d->bufsize - 5 ) 373 | { 374 | if ( !resize_buffer( d, &d->buf ) ) 375 | return; 376 | } 377 | 378 | /* 379 | * Read as much as we can. Can't wait around, since there may be other connections to attend to, 380 | * so just read as much as possible and make a note of how much that was (the socket is non-blocking 381 | * so this won't cause us to hang even if the incoming message would otherwise take time to recv) 382 | */ 383 | readsize = recv( d->sock, d->buf + start, d->bufsize - 5 - start, 0 ); 384 | 385 | /* 386 | * There's new input, successfully read and stored in memory! Let's parse it and figure out what 387 | * the heck they're asking for! (Who knows whether we've got their full transmission or whether 388 | * there's still more in the pipeline-- we'll let the parser figure that out based on the SCGI 389 | * protocol) 390 | */ 391 | if ( readsize > 0 ) 392 | { 393 | d->buflen += readsize; 394 | scgi_parse_input( d ); 395 | return; 396 | } 397 | 398 | /* 399 | * Something unexpected happened. This is the wild untamed internet, so kill the connection first and 400 | * ask questions later. 401 | */ 402 | if ( readsize == 0 || errno != EWOULDBLOCK ) 403 | { 404 | scgi_kill_socket( d ); 405 | return; 406 | } 407 | } 408 | 409 | /* 410 | * We've got a response ready for a connection, and the connection is ready to receive it. 411 | * Transmit! 412 | */ 413 | void scgi_flush_response( scgi_desc *d ) 414 | { 415 | int sent_amount; 416 | 417 | if ( !d->writehead ) 418 | d->writehead = d->outbuf; 419 | 420 | /* 421 | * Don't take too long transmitting, since other connections may be waiting. 422 | * Send as much as we can right now, and if there's more left, send the rest next time. 423 | */ 424 | sent_amount = send(d->sock, d->writehead, d->outbuflen, 0 ); 425 | 426 | /* 427 | * Transmission complete... Sayonara. 428 | */ 429 | if ( sent_amount >= d->outbuflen ) 430 | { 431 | scgi_kill_socket( d ); 432 | return; 433 | } 434 | 435 | /* 436 | * Transmission incomplete. Make a note of where we left off, we'll send the rest next time. 437 | */ 438 | d->outbuflen -= sent_amount; 439 | d->writehead = &d->writehead[sent_amount]; 440 | 441 | return; 442 | } 443 | 444 | void scgi_perror( char *txt ) 445 | { 446 | fprintf( stderr, "%s\n", txt ); 447 | return; 448 | } 449 | 450 | /* 451 | * Parse input according to the SCGI protocol. 452 | * Due to the asynchronous nature of SCGI, we may or may not have received 453 | * the full input (it might be that more is still on its way), and there's no 454 | * way to tell without parsing, so the parser must be capable of stopping, 455 | * remembering where it left off, and indicating as much (which it does via 456 | * states in the descriptor structure). 457 | */ 458 | void scgi_parse_input( scgi_desc *d ) 459 | { 460 | char *parser = &d->buf[d->parsed_chars], *end, *headername, *headerval; 461 | int len, total_req_length, headernamelen; 462 | 463 | /* 464 | * Everything has already been parsed, so do nothing until new input arrives. 465 | */ 466 | if ( d->parsed_chars == d->buflen ) 467 | return; 468 | 469 | /* 470 | * If they are not following the SCGI protocol, we have no choice but to hang up on them. 471 | * The very first character must not be 0 or : or it would be an invalid netstring 472 | * (well, technically it could be the empty netstring, but that's invalid SCGI as well, 473 | * if it's at the very start of the transmission) 474 | */ 475 | if ( d->parsed_chars == 0 && (*d->buf == '0' || *d->buf == ':') ) 476 | { 477 | scgi_kill_socket(d); 478 | return; 479 | } 480 | 481 | end = &d->buf[d->buflen]; 482 | 483 | 484 | scgi_parse_input_label: 485 | 486 | /* 487 | * How to proceed depends where we left off last time (if ever) we were parsing this input. 488 | */ 489 | switch( d->parser_state ) 490 | { 491 | case SCGI_PARSE_HEADLENGTH: // Oh yeah, we were in the middle of reading the length of their headers. (This is the default state) 492 | while ( parser < end ) 493 | { 494 | d->parsed_chars++; 495 | 496 | /* 497 | * The end of the header length is indicated by :, we've successfully read the header's length. 498 | */ 499 | if ( *parser == ':' ) 500 | { 501 | d->parser_state = SCGI_PARSE_HEADNAME; // the next task is to read the first header's name. 502 | /* 503 | * Replace the colon with an end-of-string so we can use strtoul to read the number. 504 | */ 505 | *parser = '\0'; 506 | d->true_header_length = strtoul(d->buf,NULL,10) + strlen(d->buf) + 2; 507 | *parser = ':'; // undo the end-of-string change we made above 508 | parser++; 509 | d->string_starts = parser; 510 | goto scgi_parse_input_label; 511 | } 512 | if ( *parser < '0' || *parser > '9' ) 513 | { 514 | /* 515 | * If they're trying to indicate a non-number length, they're making a mockery of the SCGI protocol, 516 | * kick them right out. 517 | */ 518 | scgi_kill_socket(d); 519 | return; 520 | } 521 | parser++; 522 | } 523 | break; 524 | 525 | case SCGI_PARSE_HEADNAME: // Oh yeah, we were in the middle of reading a header's name. 526 | while ( parser < end ) 527 | { 528 | d->parsed_chars++; 529 | 530 | if ( d->parsed_chars == d->true_header_length ) 531 | { 532 | /* 533 | * If we're supposedly at the end of the headers (based on the length they transmitted), 534 | * but the headers don't end with "\0,", then it's invalid SCGI. Been nice knowing you... 535 | */ 536 | if ( *parser != ',' || parser[-1] != '\0' ) 537 | { 538 | scgi_kill_socket(d); 539 | return; 540 | } 541 | 542 | /* 543 | * They didn't send an "SCGI" header with value 1. 544 | * Are they using some different protocol? Whatever, not our problem. Door's that way. 545 | */ 546 | if ( !d->req->scgi_scgiheader ) 547 | { 548 | scgi_kill_socket(d); 549 | return; 550 | } 551 | 552 | /* 553 | * If their headers indicated that no body is coming, then we're done. 554 | * Put the parsed request in the list of requests which have been parsed but not yet 555 | * communicated to you (the programmer of whatever program is including scgilib). 556 | */ 557 | if ( d->req->scgi_content_length == 0 ) 558 | { 559 | SCGI_CREATE( d->req->body, char, 2 ); 560 | 561 | *d->req->body = '\0'; 562 | 563 | SCGI_LINK( d->req, first_scgi_unrecved_req, last_scgi_unrecved_req, next_unrecved, prev_unrecved ); 564 | return; 565 | } 566 | len = strtoul(d->req->first_header->value,NULL,10); 567 | d->true_request_length = len + d->true_header_length; 568 | parser++; 569 | d->string_starts = parser; 570 | 571 | /* 572 | * Next task is to start reading the body after the headers 573 | */ 574 | d->parser_state = SCGI_PARSE_BODY; 575 | 576 | goto scgi_parse_input_label; 577 | } 578 | 579 | /* 580 | * A '\0' indicates the end of the header's name. 581 | */ 582 | if ( *parser == '\0' ) 583 | { 584 | /* 585 | * Of course, a header with the empty string as its name is forbidden and no such 586 | * nonsense will be tolerated. 587 | */ 588 | if ( parser == d->string_starts ) 589 | { 590 | scgi_kill_socket(d); 591 | return; 592 | } 593 | 594 | /* 595 | * Having a header's name, our next task is to parse its value. 596 | */ 597 | d->parser_state = SCGI_PARSE_HEADVAL; 598 | parser++; 599 | goto scgi_parse_input_label; 600 | } 601 | parser++; 602 | } 603 | break; 604 | 605 | case SCGI_PARSE_HEADVAL: 606 | while ( parser < end ) 607 | { 608 | d->parsed_chars++; 609 | 610 | /* 611 | * We expected a header value, and instead we reached the end of the headers (according to 612 | * the header length they specified)?! Nope.jpg 613 | */ 614 | if ( d->parsed_chars == d->true_header_length ) 615 | { 616 | scgi_kill_socket(d); 617 | return; 618 | } 619 | 620 | /* 621 | * We've successfully read the value of the current header. 622 | * Create a structure for this header and store it. 623 | */ 624 | if ( *parser == '\0' ) 625 | { 626 | headernamelen = strlen(d->string_starts); 627 | SCGI_CREATE( headername, char, headernamelen+1 ); 628 | sprintf( headername, "%s", d->string_starts ); 629 | SCGI_CREATE( headerval, char, strlen(&d->string_starts[headernamelen+1])+1 ); 630 | sprintf( headerval, "%s", &d->string_starts[headernamelen+1] ); 631 | if ( !scgi_add_header( d, headername, headerval ) ) 632 | return; 633 | /* 634 | * Next task: parse the next header's name. 635 | */ 636 | d->parser_state = SCGI_PARSE_HEADNAME; 637 | parser++; 638 | d->string_starts = parser; 639 | goto scgi_parse_input_label; 640 | } 641 | parser++; 642 | } 643 | break; 644 | 645 | case SCGI_PARSE_BODY: 646 | total_req_length = d->true_header_length + d->req->scgi_content_length; 647 | 648 | while ( parser < end ) 649 | { 650 | d->parsed_chars++; 651 | 652 | if ( d->parsed_chars == total_req_length ) 653 | { 654 | parser[1] = '\0'; 655 | SCGI_CREATE( d->req->body, char, strlen(d->string_starts)+1 ); 656 | sprintf( d->req->body, "%s", d->string_starts ); 657 | SCGI_LINK( d->req, first_scgi_unrecved_req, last_scgi_unrecved_req, next_unrecved, prev_unrecved ); 658 | 659 | return; 660 | } 661 | parser++; 662 | } 663 | break; 664 | } 665 | 666 | return; 667 | } 668 | 669 | /* 670 | * Macro to save finger leather in the following function 671 | * (repeatedly checking whether a header's name matches "match" and if so, storing its value in "address") 672 | */ 673 | #define SCGIKEY(match,address) else if (!strcmp(name,match)) do {d->req->address = val;} while(0) 674 | 675 | /* 676 | * Having read a header's name and value, attempt to make a record of it. 677 | * If something violates the SCGI protocol, kill the connection and return 0. 678 | */ 679 | int scgi_add_header( scgi_desc *d, char *name, char *val ) 680 | { 681 | scgi_header *h; 682 | 683 | /* 684 | * First header is required to be CONTENT_LENGTH and have a nonnegative numeric value. 685 | */ 686 | if ( !d->req->first_header ) 687 | { 688 | if ( strcmp( name, "CONTENT_LENGTH" ) 689 | || !scgi_is_number( val ) ) 690 | { 691 | free( name ); 692 | free( val ); 693 | scgi_kill_socket(d); 694 | return 0; 695 | } 696 | 697 | d->req->scgi_content_length = strtoul(val,NULL,10); 698 | 699 | if ( d->req->scgi_content_length < 0 ) 700 | { 701 | free( name ); 702 | free( val ); 703 | scgi_kill_socket(d); 704 | return 0; 705 | } 706 | } 707 | 708 | SCGI_CREATE( h, scgi_header, 1 ); 709 | h->name = name; 710 | h->value = val; 711 | 712 | SCGI_LINK( h, d->req->first_header, d->req->last_header, next, prev ); 713 | 714 | /* 715 | * Certain headers' values have space allocated especially for them in the request structure... 716 | */ 717 | 718 | if ( !strcmp( name, "SCGI" ) && !strcmp( val, "1" ) ) 719 | d->req->scgi_scgiheader = 1; 720 | 721 | if ( !strcmp(name, "HTTP_COOKIE") ) 722 | { 723 | d->req->raw_http_cookie = val; 724 | } 725 | else if ( !strcmp(name, "REQUEST_METHOD" ) ) 726 | { 727 | if ( !strcmp(val,"GET") ) d->req->request_method = SCGI_METHOD_GET; 728 | else if ( !strcmp(val,"POST") ) d->req->request_method = SCGI_METHOD_POST; 729 | else if ( !strcmp(val,"HEAD") ) d->req->request_method = SCGI_METHOD_HEAD; 730 | else d->req->request_method = SCGI_METHOD_UNKNOWN; 731 | } 732 | SCGIKEY("HTTP_CONNECTION", http_connection); 733 | SCGIKEY("HTTP_CACHE_CONTROL", http_cache_control); 734 | SCGIKEY("HTTP_ACCEPT_CHARSET", http_accept_charset); 735 | SCGIKEY("HTTP_ACCEPT_ENCODING", http_accept_encoding); 736 | SCGIKEY("HTTP_ACCEPT_LANGUAGE", http_accept_language); 737 | SCGIKEY("HTTP_ACCEPT", http_accept ); 738 | SCGIKEY("HTTP_USER_AGENT", user_agent ); 739 | SCGIKEY("USER_AGENT", user_agent ); 740 | SCGIKEY("HTTP_HOST", http_host ); 741 | SCGIKEY("QUERY_STRING", query_string ); 742 | SCGIKEY("REQUEST_URI", request_uri ); 743 | SCGIKEY("REMOTE_ADDR", remote_addr ); 744 | SCGIKEY("SERVER_ADDR", server_addr ); 745 | SCGIKEY("SERVER_PORT", server_port ); 746 | SCGIKEY("SERVER_PROTOCOL", server_protocol ); 747 | 748 | return 1; 749 | } 750 | 751 | /* 752 | * Function to check whether a string is a number 753 | */ 754 | int scgi_is_number( char *arg ) 755 | { 756 | int first = 1; 757 | if ( *arg == '\0' ) 758 | return 0; 759 | 760 | for ( ; *arg != '\0'; arg++ ) 761 | { 762 | if ( first && *arg == '-') 763 | { 764 | first = 0; 765 | continue; 766 | } 767 | if ( !isdigit(*arg) ) 768 | return 0; 769 | first = 0; 770 | } 771 | 772 | return 1; 773 | } 774 | 775 | void scgi_deal_with_socket_out_of_ram( scgi_desc *d ) 776 | { 777 | scgi_kill_socket( d ); 778 | } 779 | 780 | /* 781 | * Function to initialize the SCGI C Library (and start it listening on the specified port). 782 | * Returns 0 on failure. 783 | * May be called multiple times with different port numbers, if you want a single program 784 | * listening on different ports. 785 | * 786 | * This is one of the functions which you (the programmer making use of the SCGI C Library) 787 | * are likely to use in practice. 788 | */ 789 | int scgi_initialize(int port) 790 | { 791 | scgi_port *p; 792 | int status, sock; 793 | struct addrinfo hints, *servinfo; 794 | char portstr[128]; 795 | 796 | /* 797 | * Socket stuff 798 | */ 799 | memset(&hints, 0, sizeof hints); 800 | hints.ai_family = AF_UNSPEC; 801 | hints.ai_socktype = SOCK_STREAM; 802 | hints.ai_flags = AI_PASSIVE; 803 | sprintf( portstr, "%d", port ); 804 | 805 | if ((status=getaddrinfo(NULL,portstr,&hints,&servinfo)) != 0) 806 | { 807 | return 0; 808 | } 809 | 810 | sock = socket( servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol ); 811 | 812 | if ( sock == -1 ) 813 | return 0; 814 | 815 | if ( bind(sock, servinfo->ai_addr, servinfo->ai_addrlen) == -1 816 | || listen(sock, SCGI_LISTEN_BACKLOG_PER_PORT) == -1 ) 817 | { 818 | close(sock); 819 | return 0; 820 | } 821 | 822 | /* 823 | * At this point, SCGI C Library has successfully opened its ears to listen on the specified port. 824 | * Commit the port to memory. 825 | */ 826 | 827 | SCGI_CREATE( p, scgi_port, 1 ); 828 | p->next = NULL; 829 | p->prev = NULL; 830 | p->first_scgi_desc = NULL; 831 | p->last_scgi_desc = NULL; 832 | p->port = port; 833 | p->sock = sock; 834 | 835 | SCGI_LINK(p, first_scgi_port, last_scgi_port, next, prev ); 836 | 837 | return 1; 838 | } 839 | 840 | /* 841 | * If any pending requests have successfully been parsed and are waiting to be served by your program, 842 | * this function will return a pointer to one of them. A NULL return value indicates there are no such 843 | * requests. 844 | * 845 | * This is one of the functions which you (the programmer making use of the SCGI C Library) 846 | * are likely to use in practice. 847 | */ 848 | scgi_request *scgi_recv( void ) 849 | { 850 | scgi_request *req; 851 | 852 | if ( !first_scgi_unrecved_req ) 853 | { 854 | scgi_update_connections(); 855 | 856 | if ( !first_scgi_unrecved_req ) 857 | return NULL; 858 | } 859 | 860 | req = first_scgi_unrecved_req; 861 | 862 | /* 863 | * After scgi_recv returns the pointer to the request, it is up to you (the programmer using SCGI Library) 864 | * to do something with it-- in most cases by sending a response. 865 | */ 866 | req->descriptor->state = SCGI_SOCKSTATE_WRITING_RESPONSE; 867 | 868 | SCGI_UNLINK( req, first_scgi_unrecved_req, last_scgi_unrecved_req, next_unrecved, prev_unrecved ); 869 | 870 | return req; 871 | } 872 | 873 | /* 874 | * Send a response to a request, without explicitly specifying the response's length. 875 | * NOTE: scgi_write should only be called once per request. Once it has been called, every time 876 | * the SCGI Library updates it will send as much of the response as it can, until the whole 877 | * response has been sent, and at that time, the request will be free'd. 878 | * 879 | * Returns 0 in case of failure due to inability to allocate RAM. 880 | * 881 | * This is one of the functions which you (the programmer making use of the SCGI C Library) 882 | * are likely to use in practice. 883 | */ 884 | int scgi_write( scgi_request *req, char *txt ) 885 | { 886 | int len = strlen(txt); 887 | 888 | return scgi_send( req, txt, len ); 889 | } 890 | 891 | /* 892 | * Send a response to a request, explicitly specifying the response's length. 893 | * NOTE: scgi_send should only be called once per request. Once it has been called, every time 894 | * the SCGI Library updates it will send as much of the response as it can, until the whole 895 | * response has been sent, and at that time, the request will be free'd. 896 | * 897 | * Returns 0 in case of failure due to inability to allocate RAM. 898 | * 899 | * This is one of the functions which you (the programmer making use of the SCGI C Library) 900 | * are likely to use in practice. 901 | */ 902 | int scgi_send( scgi_request *req, char *txt, int len ) 903 | { 904 | scgi_desc *d = req->descriptor; 905 | 906 | /* 907 | * If more is being sent than we've allocated space for, then allocate more space 908 | */ 909 | if ( len >= d->outbufsize - 5 ) 910 | { 911 | char *newbuf = calloc( len + 6, sizeof(char) ); 912 | 913 | if ( !newbuf ) 914 | return 0; 915 | memcpy( newbuf, txt, len ); 916 | free( d->outbuf ); 917 | d->outbuf = newbuf; 918 | d->outbuflen = len; 919 | d->outbufsize = len + 6; 920 | return 1; 921 | } 922 | 923 | memcpy( d->outbuf, txt, len ); 924 | d->outbuflen = len; 925 | 926 | /* 927 | * The actual physical transmission will be handled by the scgi_flush_response function, 928 | * once the socket is ready to receive it. 929 | */ 930 | 931 | return 1; 932 | } 933 | 934 | 935 | void scgi_302_redirect( scgi_request *req, char *address ) 936 | { 937 | char buf[256], *b = buf; 938 | int len; 939 | 940 | len = 100 + strlen(address); 941 | 942 | if ( len >= 250 ) 943 | { 944 | SCGI_CREATE( b, char, len + 1 ); 945 | } 946 | 947 | sprintf( b, "Status: 302 Found\n\rLocation: %s\n\rContent-Length: 0\n\r\n\r", address ); 948 | scgi_send( req, b, len ); 949 | 950 | if ( b != buf ) 951 | free( b ); 952 | } 953 | -------------------------------------------------------------------------------- /scgilib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SCGI C Library 3 | * By Sam Alexander 4 | * 5 | * Version 0.2, Last Updated: 26 July 2015 6 | * 7 | * scgilib.h - SCGI C Library header file 8 | * 9 | * Instructions: #include this library file into whatever project you're working on. 10 | * Compile your project along with the accompanying scgilib.c file. 11 | * Use the library's functions to your heart's content. 12 | * 13 | * Copyright/license: MIT. 14 | */ 15 | 16 | #ifndef SCGILIB_H 17 | #define SCGILIB_H 18 | 19 | #include 20 | #include 21 | 22 | typedef struct SCGI_PORT scgi_port; 23 | typedef struct SCGI_HEADER scgi_header; 24 | typedef struct SCGI_REQUEST scgi_request; 25 | typedef struct SCGI_DESC scgi_desc; 26 | 27 | #if !defined(FNDELAY) 28 | #define FNDELAY O_NDELAY 29 | #endif 30 | 31 | /* 32 | * If a browser connects, but doesn't do anything, how long until kicking them off 33 | * (See also the next comment below) 34 | */ 35 | #define SCGI_KICK_IDLE_AFTER_X_SECS 60 36 | 37 | /* 38 | * How many times, per second, will your main project be checking for new connections? 39 | * (Rather than keep track of the exact time a client is idle, rather we keep track of 40 | * the number of times we've checked for updates and found none. When the client has 41 | * been idle for SCGI_KICK_IDLE_AFTER_X_SECS * SCGI_PULSE_PER_SEC consecutive checks, 42 | * they will be booted. SCGI_PULSES_PER_SEC is not used anywhere else. Thus, it is 43 | * not terribly important that it be completely precise, a ballpark estimate is good 44 | * enough. 45 | */ 46 | #define SCGI_PULSES_PER_SEC 10 47 | 48 | /* 49 | * Different states of a client. 50 | */ 51 | #define SCGI_SOCKSTATE_READING_REQUEST 0 52 | #define SCGI_SOCKSTATE_WRITING_RESPONSE 1 53 | 54 | /* 55 | * How many bytes of memory to initially allocate for I/O buffers when a client connects. 56 | * (These will automatically grow when/if the client sends a bigger amount of input or 57 | * SCGI C Library responds with a bigger amount of output) 58 | */ 59 | #define SCGI_INITIAL_OUTBUF_SIZE 16384 60 | #define SCGI_INITIAL_INBUF_SIZE 16384 61 | 62 | /* 63 | * Upper limits on the size of I/O buffers. If they send more data than this, 64 | * or compel us to send a bigger response, SCGI C Library kills the connection. 65 | */ 66 | #define SCGI_MAX_INBUF_SIZE 131072 67 | #define SCGI_MAX_OUTBUF_SIZE 524288 68 | 69 | /* 70 | * If multiple clients simultaneously attempt to connect, how many connections should SCGI C Library 71 | * accept at once? Additional simultaneous connections beyond this limit will have to wait 72 | * their turn. 73 | */ 74 | #define SCGI_LISTEN_BACKLOG_PER_PORT 32 75 | 76 | /* 77 | * Different parts of the SCGI protocol 78 | */ 79 | typedef enum 80 | { 81 | SCGI_PARSE_HEADLENGTH, SCGI_PARSE_HEADNAME, SCGI_PARSE_HEADVAL, 82 | SCGI_PARSE_BODY 83 | } types_of_states_for_the_request_parser; 84 | 85 | /* 86 | * Different HTTP request types 87 | * (right now the SCGI C Library is mainly only built for GET and HEAD) 88 | */ 89 | typedef enum 90 | { 91 | SCGI_METHOD_UNSPECIFIED, SCGI_METHOD_UNKNOWN, 92 | SCGI_METHOD_GET, SCGI_METHOD_POST, SCGI_METHOD_HEAD 93 | } types_of_methods_for_http_protocol; 94 | 95 | /* 96 | * Macros for handling generic doubly-linked lists 97 | */ 98 | #define SCGI_LINK(link, first, last, next, prev) \ 99 | do \ 100 | { \ 101 | if ( !(first) ) \ 102 | { \ 103 | (first) = (link); \ 104 | (last) = (link); \ 105 | } \ 106 | else \ 107 | (last)->next = (link); \ 108 | (link)->next = NULL; \ 109 | if ((first) == (link)) \ 110 | (link)->prev = NULL; \ 111 | else \ 112 | (link)->prev = (last); \ 113 | (last) = (link); \ 114 | } while(0) 115 | 116 | #define SCGI_UNLINK(link, first, last, next, prev) \ 117 | do \ 118 | { \ 119 | if ( !(link)->prev ) \ 120 | { \ 121 | (first) = (link)->next; \ 122 | if ((first)) \ 123 | (first)->prev = NULL; \ 124 | } \ 125 | else \ 126 | { \ 127 | (link)->prev->next = (link)->next; \ 128 | } \ 129 | if ( !(link)->next ) \ 130 | { \ 131 | (last) = (link)->prev; \ 132 | if((last)) \ 133 | (last)->next = NULL; \ 134 | } \ 135 | else \ 136 | { \ 137 | (link)->next->prev = (link)->prev; \ 138 | } \ 139 | } while (0) 140 | 141 | /* 142 | * Data structure for a port -- SCGI C Library can listen on multiple ports simultaneously 143 | */ 144 | struct SCGI_PORT 145 | { 146 | scgi_port *next; 147 | scgi_port *prev; 148 | scgi_desc *first_scgi_desc; // first descriptor, i.e. connection (in a doubly-linked list) 149 | scgi_desc *last_scgi_desc; // last descriptor, i.e. connection (in a doubly-linked list) 150 | int port; // port number 151 | int sock; // socket number for listening on this port 152 | }; 153 | 154 | /* 155 | * Data structure for a header in the SCGI protocol 156 | */ 157 | struct SCGI_HEADER 158 | { 159 | scgi_header *next; 160 | scgi_header *prev; 161 | char *name; // name of the header 162 | char *value; // value of the header 163 | }; 164 | 165 | /* 166 | * Data structure for a successfully parsed SCGI request. 167 | * This is what projects using the SCGI C Library will primarily interact with. 168 | */ 169 | struct SCGI_REQUEST 170 | { 171 | scgi_request *next; 172 | scgi_request *prev; 173 | scgi_request *next_unrecved; 174 | scgi_request *prev_unrecved; 175 | scgi_desc *descriptor; // info about the connection 176 | scgi_header *first_header; // doubly-linked list of request headers 177 | scgi_header *last_header; 178 | char *body; // request body 179 | int scgi_content_length; // length of the request body 180 | char scgi_scgiheader; // whether or not the request included the "SCGI" header 181 | int *dead; // pointer to an int which SCGI C Library can use to specify whether a connection is dead (see documentation for details) 182 | int request_method; // type of request (SCGI_METHOD_GET, SCGI_METHOD_POST, SCGI_METHOD_HEAD, or SCGI_METHOD_UNKNOWN) 183 | char *http_host; // which host name are they connecting to (in principle, with this, you can have one program serve multiple domain names) 184 | /* 185 | * The remaining fields are some individual headers that might be sent 186 | */ 187 | char *query_string; 188 | char *request_uri; 189 | char *http_cache_control; 190 | char *raw_http_cookie; 191 | char *http_connection; 192 | char *http_accept_encoding; 193 | char *http_accept_language; 194 | char *http_accept_charset; 195 | char *http_accept; 196 | char *user_agent; 197 | char *remote_addr; // Client's IP address 198 | char *server_port; 199 | char *server_addr; 200 | char *server_protocol; 201 | }; 202 | 203 | /* 204 | * Info about a connection 205 | */ 206 | struct SCGI_DESC 207 | { 208 | scgi_desc *next; 209 | scgi_desc *prev; 210 | scgi_port *port; //which port are they connected to 211 | scgi_request *req; //info about the request they are sending 212 | int sock; //which socket they're bound to 213 | char *buf; //input buffer for the data they're sending us 214 | int bufsize; //how much space we've allocated so far for the data they're sending us 215 | int buflen; //how much data they've sent us so far 216 | char *outbuf; //output buffer for data we're going to send them 217 | int outbufsize; //how much space we've allocated for outbuf so far 218 | int outbuflen; //how long outbuf has become so far 219 | int idle; //how many times we checked the connection for new data and found it idle 220 | int state; //which state is this connection in 221 | char *writehead; //pointer to the end of the data currently stored in outbuf 222 | /* 223 | * The remaining fields are technical fields used by the parser 224 | */ 225 | int parsed_chars; 226 | char *string_starts; 227 | int true_header_length; 228 | int true_request_length; 229 | int parser_state; 230 | }; 231 | 232 | /* 233 | * Global variables from scgilib.c 234 | */ 235 | extern scgi_port *first_scgi_port; // doubly-linked list of ports for SCGI C Library to listen on 236 | extern scgi_port *last_scgi_port; 237 | 238 | extern scgi_request *first_scgi_req; // doubly-linked list of SCGI requests awaiting response 239 | extern scgi_request *last_scgi_req; 240 | 241 | extern fd_set scgi_inset; // socket programming stuff 242 | extern fd_set scgi_outset; 243 | extern fd_set scgi_excset; 244 | 245 | /* 246 | * Function prototypes for functions from scgilib.c 247 | */ 248 | void scgi_update_connections_port( scgi_port *p ); 249 | void scgi_update_connections( void ); 250 | void scgi_kill_socket( scgi_desc *d ); 251 | void free_scgi_request( scgi_request *r ); 252 | void scgi_flush_response( scgi_desc *d ); 253 | void scgi_listen_to_request( scgi_desc *d ); 254 | void scgi_answer_the_phone( scgi_port *p ); 255 | void scgi_perror( char *txt ); 256 | int scgi_initialize(int port); 257 | int scgi_send( scgi_request *req, char *txt, int len ); 258 | int scgi_write( scgi_request *req, char *txt ); 259 | scgi_request *scgi_recv( void ); 260 | 261 | /* 262 | * Memory allocation macro 263 | */ 264 | #define SCGI_CREATE(result, type, number) \ 265 | do \ 266 | { \ 267 | if (!((result) = (type *) calloc ((number), sizeof(type)))) \ 268 | { \ 269 | fprintf(stderr, "scgilib: Out of RAM! Emergency shutdown.\n" ); \ 270 | abort(); \ 271 | } \ 272 | } while(0) 273 | 274 | #endif //ends the "#ifdef SCGILIB_H" from the beginning of the file 275 | --------------------------------------------------------------------------------