/************************************************************************/ /* commhttpserver.c */ /************************************************************************/ /* * (c)2003, 2004, 2008, 2019 Todd Shadburn * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /************************************************************************/ #include #include #include #include #include #include #include #include #define BUFFER_SIZE 256 int handle_io (CommChannel *cc, int iotype, void *ccarg); int timeout_callback(CommGroup *cg, void *arg); /* * In this example we make the comm group and the server connection global * variables for convenience, so we have access to them in the * handle_io() function. */ CommGroup *CG; CommChannel *ccserver; char static_response[] = "HTTP/1.1 200 Ok\r\nServer: commhttpserver\r\nContent-Length: 22\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nThis is a test page.\r\n"; int static_response_len; uint32_t requests_count; int main (int argc, char ** argv) { int rc; CommModule *cm = NULL; Attribute *commattribs = NULL; CG = NULL; ccserver = NULL; static_response_len = strlen(static_response); requests_count = 0; /* * Create a new comm group */ fprintf (stderr, "calling comm_group_new\n"); rc = comm_group_new (&CG, handle_io); if (rc != 0) { fprintf (stderr, "comm_group_new: rc = %d\n", rc); return rc; } /* * Load the required plugin: * We specify a relative plugin path for this test app, because * the library has not been installed yet. * Real applications would specify only the plugin name * without an extention (.so, .dll, etc) */ fprintf (stderr, "calling comm_plugin_load\n"); rc = comm_plugin_load (&cm, "/usr/local/lib/comm_tcp.so", commattribs); if (rc != 0) { fprintf (stderr, "comm_plugin_load: rc = %d\n", rc); return rc; } /* * Now create a "server" socket, bound to a specific port */ fprintf (stderr, "calling comm_bind\n"); rc = comm_bind (cm, &ccserver, NULL, 8080); if (rc != 0) { fprintf (stderr, "comm_bind: rc = %d\n", rc); rc = comm_plugin_unload (&cm); return rc; } /* * Add the server connection to the comm group so it's I/O * can be handled by the group's I/O handler function */ fprintf (stderr, "calling comm_group_add\n"); rc = comm_group_add (CG, ccserver, NULL, COMM_IO_READ); if (rc != 0) { fprintf (stderr, "comm_group_add: rc = %d\n", rc); comm_group_remove (CG, ccserver); comm_group_free (&CG); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } /* * We ignore this for the example, but a real application may * want to catch/handle this */ signal (SIGPIPE, SIG_IGN); /* * Setup a timeout callback and the timeout value */ fprintf (stderr, "calling comm_group_set_timeout_callback\n"); rc = comm_group_set_timeout_callback (CG, timeout_callback, NULL); if (rc != 0) { fprintf (stderr, "comm_group_set_timeout_callback: rc = %d\n", rc); comm_group_remove (CG, ccserver); comm_group_free (&CG); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } rc = comm_group_set_option(CG, COMM_OPTION_TIMEOUT, 60000000); if (rc != 0) { fprintf (stderr, "comm_group_set_option: rc = %d\n", rc); comm_group_remove (CG, ccserver); comm_group_free (&CG); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } /* * enter the loop, handling I/O using the handle_io() function */ rc = comm_group_loop (CG); /* * Cleanup: Deallocate the comm group resources */ fprintf (stderr, "calling comm_group_free\n"); rc = comm_group_free (&CG); if (rc != 0) { fprintf (stderr, "comm_group_free: rc = %d\n", rc); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } /* * Cleanup: Deallocate the server connection resources */ fprintf (stderr, "calling comm_close(server)\n"); rc = comm_close (&ccserver, 0); if (rc != 0) { fprintf (stderr, "comm_close(server): rc = %d\n", rc); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } /* * Cleanup: Deallocate the plugin resources */ fprintf (stderr, "calling comm_plugin_unload\n"); rc = comm_plugin_unload (&cm); if (rc != 0) { fprintf (stderr, "comm_plugin_unload: rc = %d\n", rc); rc = comm_close (&ccserver, 0); rc = comm_plugin_unload (&cm); return rc; } return 0; } int handle_io (CommChannel *cc, int iotype, void *ccarg) { int rc; int inlen; int ccflags; char inbuf[BUFFER_SIZE]; CommChannel *ccclient = NULL; if (cc == ccserver) { //fprintf (stderr, "handle_io(server)(iotype=%d)\n", iotype); /* * Handle a new connection on the server socket */ /* * Accept the client connection */ rc = comm_accept (cc, &ccclient, 0); if (rc != COMM_RC_SUCCESS && rc != COMM_RC_WANT_READ && rc != COMM_RC_WANT_WRITE) { //fprintf (stderr, // "comm_accept: rc = %d\n", // rc); return rc; } /* * Non-blocking I/O could mean that the "accept" isn't * complete yet, so we need to have the loop "listen" * for the client connection to become write'able again * (ie. Our socket buffer was full before all our data * could be sent) */ if (rc == COMM_RC_WANT_WRITE) rc = comm_set_ioneeds (ccclient, COMM_IO_WRITE); /* * Add the new client connection to the comm group */ rc = comm_group_add (CG, ccclient, NULL, COMM_IO_READ); } else { //fprintf (stderr, "handle_io(client)(iotype=%d)\n", iotype); /* * Handle I/O on the client socket * This example acts as a simple echo server, whatever data the * client sends to us, we just echo it back to the client */ /* * We clear the WRITE flag, if it was set for the client */ if (iotype == COMM_IO_WRITE) rc = comm_set_ioneeds (cc, 0); /* * If the accept() wasn't complete, continue with it */ rc = comm_get_flags (cc, &ccflags); if (ccflags & COMMFLAG_INACCEPT) { rc = comm_accept_continue (cc); return rc; } /* * Read from the client connection */ inlen = BUFFER_SIZE; rc = comm_read (cc, inbuf, &inlen); if (rc != 0) { switch(rc) { /* * we're need more data from the client, * so just return COMM_RC_SUCCESS */ case COMM_RC_WANT_READ: case COMM_RC_CALL_AGAIN: //fprintf (stderr, // "server: comm_read: WANT_READ\n"); return COMM_RC_SUCCESS; break; /* * We couldn't send all our data to the client, * so tell the loop to notify us when we can * write more and then return COMM_RC_SUCCESS */ case COMM_RC_WANT_WRITE: //fprintf (stderr, // "server: comm_read: WANT_WRITE\n"); rc = comm_set_ioneeds (cc, COMM_IO_WRITE); return COMM_RC_SUCCESS; break; default: /* * Something unexpected happened, * close the client connection */ comm_group_remove (CG, cc); comm_close (&cc, 0); /* if (rc == 61) return 0; */ fprintf (stderr, "server: comm_read: rc = %d\n", rc); return rc; } } /* * Echo the data back to the client connection * In a real application, comm_write() could return * COMM_RC_WANT_WRITE/COMM_RC_WANT_READ, so that should * be handled too. Our example just closes the connection * to that client because we don't maintain the client's * state. */ inlen = static_response_len; rc = comm_write (cc, static_response, &inlen); if (rc != 0) { //fprintf (stderr, "server: comm_write: rc = %d\n", rc); comm_group_remove (CG, cc); comm_close (&cc, 0); if (rc != EIO) { return rc; } //fprintf (stderr, "server: comm_write: rc = EIO\n"); } else { // successfully wrote response, close connection comm_group_remove (CG, cc); comm_close (&cc, 0); requests_count++; } } return 0; } int timeout_callback(CommGroup *cg, void *arg) { fprintf(stdout, "Timeout callback called: requests_count=%d\n", requests_count); return 0; }