This wikipage is dedicated to GoAhead Open Source Embedded Webserver.
For more information visit the main page.
On Unix systems (like Linux) if the webserver is started from the command line it will stop working after closing the terminal.
There are 2 methods to avoid this to happen:
$nohup webs &
pid_t pid; pid = fork(); switch ( pid ) { case -1: /* fork() failed -- exit with an error status */ perror("forking daemon"); break; case 0: /* we're the child process -- carry on... */ break; default: /* we're the parent process -- exit with success status */ exit(0); break; } if ( setsid() < 0 ) { perror("setsid()"); }
A method to track user state during web browsing.
You have to add a Set-Cookie: HTTP pragma when replying to a form submission or use http meta-equiv tags in the generated page.
For implementation you have the following options:
See the following links for more information:
Session support solution from csyap using user/pass for storage.
Using the information from the wp structure a hash identifier for the curent user can be generated (based on IP and other browser supplied information).
By default most http content (images, static html files) are cached. This means that after the first visit the files are retrieved from the local cache and not from the web server.
In some cases this is not desirable so we'll describe here methods to disable content caching.
The following HTTP protocol pragma's are automatically add to ASP pages (dynamic pages):
Pragma: no-cache Cache-control: no-cache
This should work with most browser clients although according to HTTP/1.0 these headers are intended for HTTP proxies.
Another method that can be used to prevent caching is to use the following HTML directives in the web page header.
<meta http-equiv="Cache-Control" content="no-cache"> <meta http-equiv="Pragma" content="no-cache">
which help with the users browser cache but not with most proxy servers.
Special notes regarding IE:
If you suspect problems at the HTTP communication level you can try out the following to debug the problem:
# Server running on port 80 $telnet www.prolix.ro 80 GET / HTTP/1.0<ENTER> <ENTER>
The ENTER key must be pressed twice, required by the HTTP protocol specification. This gives you access to the HTTP protocol headers the same way as method #2.
There are 2 methods to do this.
The file upload patch is listed on the GoAhead website.
Latest version 1.1.4 requires some modifications to work with GoAhead Webserver 2.1.8.
It compiles without errors with version 2.1.7.
To understand how this works take a look at the patch. There is an upload goform and the uploaded file will be stored in the default path (check the source code).
Using a CGI script that will perform the file upload. Make sure you apply this fix first.
There are 2 methods that you can use:
Basic Access Authentication is an authentication method that is supported with all browsers.
When a resource is protected by BAA the webserver will send back to the client first: 401 - Authentication Required
Upon receiving this the browser will popup a window requiring the USER/PASSWORD from the client.
Because HTTP is stateless the browser will be responsable for prepending the user/pass in all requests (URL) for the given browsing session.
The user credentials are sent in plain text so this is not a safe communication method, the password can be retrieved easily with a sniffer. If security is important make sure you use SSL or DAA.
Digest Access Authentication is more secure because it's using a hashing scheme to send user/pass credential.
It should work correctly with all modern browsers.
Configuration instructions:
umAddAccessLimit(T("admin/", AM_DIGEST, 0, T("admin"));
in the initialization code for the WebServer.
IMPORTANT:
Brett G. Porter, 2002/01/17
At this moment it is not possible to make a user part of multiple groups.
A solution is to add hierarchical security using numbers as group names where a higher number means that members on that group are more privileged.
The checking user credentials is done in um.c:umUserCanAccessUrl():
/* * If Access Limit has a group specified, then the user must be a * member of that group */ if (group && *group) { if (usergroup && (gstrcmp(group, usergroup) != 0)) { return FALSE; } }
We're doing a lexicographic compare to see if the user's group is an exact match for the group assigned to the URL.
To make this hierarchical with a minimum of work use numerical group names instead of symbolic and change the above code to something like:
/* * If Access Limit has a group specified, then the user must have an * access level that high or higher. */ if (group && *group) { if (usergroup) { // if this URL requires a higher access level than this // user has, deny them access. if (gatoi(group) > gatoi(usergroup)) return FALSE; } }
Assign:
I've worked on systems with 255 levels of access available, but never ended up using more than 3. If you're more motivated, you might think about modifying the code so that you can use a symbolic group name and assign an access level to the group name.
In case you made the necessary changes and the authentication is still not working try out the following:
WEBS_LOCAL_REQUEST flag in the webserver code.Posted by Null Peiler, 2004/09/09
In case the documentation does not explain clearly how to perform this step, here is another tutorial:
home.asp
welcome.htm
images/myfirstimage.gif
images/mysecondimage.jpg
...
#define WEBS_PAGE_ROM
Free
Commercial
John Reynolds, Loren, Gabriel Petchesi 11/01/05
You can use two type of certificates:
We explain here how to generate a self signed certificate for the GoAhead web server using openssl and the Linux shell.
1. Create a key and X.509 certificate:
$openssl req -x509 -newkey rsa:2048 -days 1024 -keyout server-key.pem -out server-cert.pem
The options that can be changed here are:
You will be prompted for the PEM pass phrase twice for the key and than you have to enter some information necessary for the certificate:
Country Name <US> RO State or Province Name <YourState> Bihor City or Locality <Anchorage> Oradea Organization Name <Your business name> Prolix Organizational Unit leave blank -- just hit <enter> Software Development Common Name (SERVER HOST NAME) <www.yourdomain.dom> www.prolix.ro Server Admin's email address <you@yourdomain.dom> gabi@prolix.ro
It's important to specify correctly the Common Name. Wildcards can be used for domain name like *.prolix.ro for a certificate valid for all subdomains.
2. Strip pass phrase:
$openssl rsa -in server-key.pem -out server-key-nopassword.pem
3. Combine the key and X.509 certificate files into server.pem:
$cat server-key-nopassword.pem server-cert.pem > server.pem
4. Move them relative to the directory where the webs is started, in case of the LINUX build that is:
server.pem -> LINUX/server.pem server-cert.pem -> LINUX/certs/cacert.pem server-key.pem -> LINUX/certs/cakey.pem
The certitificate files are searched relative to the current directory. You must first change directory to LINUX and than start up webs.
If you are in another dir you get the following error:
SSL: Initializing SSL SSL: Unable to set cert verification locations! SSL: Closing SSL
Published originally by Lee Dilkie:
openssl x509 -C -in my_cert_and_public_key_file.pem >cert.c
openssl rsa -inform PEM -outform DER -in my_private_key_file.pem >key.der bintoc key.der dey.c
The source code for bintoc binary: bintoc.cpp stdafx.h needed to compile the app on Windows.
POST here your experiences after applying these changes.
Server Side Includes is a method to include content from other pages giving to a site a consistent look.
It's often used to have the same header/footer for all webpages.
This feature is not supported by the GoAhead webserver. See the following section for methods to simulate this.
Shahid Mahmood, 2001/10/15
SSI features can be accoumplished using ASP. Here is what I do:
<%myVar=1234;%>
<%asp_updateMyVar();%>
websAspDefine(T("aspTest"), aspTest);
It can be in main.c or or in a new file (I have a new file for all my asp functions). The asp_updateMyVar() must follow same prototype as aspTest().
Robert Light, 2000/11/09
Define an ASP function to include the content of another file like in the following example:
<BODY><H1><%include('otherfile');%></H1><BODY>
Implementation:
int include( int eid, webs_t wp, int argc, char_t **argv) { char* path; char lpath[512]; if (ejArgs(argc, argv, T("%s"), &path) < 1) { websError(wp, 400, T("Insufficient args\n")); return -1; } sprintf(lpath,"%s/%s",websGetDefaultDir(),path); if (websPageOpen(wp, lpath, path, O_RDONLY | O_BINARY, 0666) < 0) { websError(wp, 400, T("Cannot open include file: <b>%s</b>"), path); return 1; } if (websAspRequest(wp, lpath) < 0) return 1; websPageClose(wp); return 1; }
The port number is defined in the main.c file of your platform (port variable) and by default it's value is 80 (http port).
For UNIX change this value to something bigger than 1024 (8080) so that users will not need root access to start up the webserver.
The rootWeb variable from main.c defines the dir where the html files are looked up. This a value relative to the current working directory. If you want to set up an absolute path like:
static char_t *rootWeb = T("/var/local/webpages"); /* Root web directory */
you also need to make another change in main.c:
/* * Set ../web as the root web. Modify this to suit your needs */ + /* getcwd(dir, sizeof(dir)); if ((cp = strrchr(dir, '/'))) { *cp = '\0'; } sprintf(webdir, "%s/%s", dir, rootWeb); + */ + sprintf(webdir, "%s", rootWeb);
Report by newzy@hotmail.com, 2006/3/1
http://www.eybuild.com
I like write CGI with C with the tool ”eybuild”(http://www.eybuild.com), in which insert C code HTML directly(also call CSP). But when I write CGI and run in GoAhead, I found GoAhead doesn't support “Status”, “Location” in MIME header. So I porting this part from mini-httpd server.
To support “Status”, “Location” like apache in GoAhead CGI Script, in web.c websCgiGatherOutput (cgiRec *cgip) I change following:
+ glseek(fdout, cgip->fplacemark, SEEK_SET); /* move to here */
if (cgip->fplacemark == 0) {
+ struct web_cgi web_cgi = {fdout, cgip, wp};
+ cgi_interpose_output(&web_cgi , TRUE);
- websWrite(wp, T("HTTP/1.0 200 OK\r\n"));
}
- glseek(fdout, cgip->fplacemark, SEEK_SET);
Add following code before function websCgiGatherOutput()(NOTE most of following come from mini-httpd):
static void*
e_malloc( size_t size )
{
void* ptr;
ptr = malloc( size );
if ( ptr == (void*) 0 )
{
//syslog( LOG_CRIT, "out of memory" );
//(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
return ptr;
}
static void*
e_realloc( void* optr, size_t size )
{
void* ptr;
ptr = realloc( optr, size );
if ( ptr == (void*) 0 )
{
//syslog( LOG_CRIT, "out of memory" );
//(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
return ptr;
}
static void
add_to_buf( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len )
{
if ( *bufsizeP == 0 )
{
*bufsizeP = len + 500;
*buflenP = 0;
*bufP = (char*) e_malloc( *bufsizeP );
}
else if ( *buflenP + len >= *bufsizeP )
{
*bufsizeP = *buflenP + len + 500;
*bufP = (char*) e_realloc( (void*) *bufP, *bufsizeP );
}
(void) memmove( &((*bufP)[*buflenP]), str, len );
*buflenP += len;
(*bufP)[*buflenP] = '\0';
}
#define ssize_t int
#define snprintf _snprintf
struct web_cgi {
int fdout;
cgiRec * cgip;
webs_t wp;
};
/* This routine is used for parsed-header CGIs and for all SSL CGIs. */
static void
cgi_interpose_output(struct web_cgi * pcgi, int parse_headers )
{
ssize_t r;
char buf[1024];
int rfd = pcgi->fdout;
cgiRec * cgip = pcgi->cgip;
webs_t wp = pcgi->wp;
if ( ! parse_headers )
{
/* If we're not parsing headers, write out the default status line
** and proceed to the echo phase.
*/
char http_head[] = "HTTP/1.0 200 OK\015\012";
//(void) my_write( http_head, sizeof(http_head) );
(void) websWrite(wp, http_head);
}
else
{
/* Header parsing. The idea here is that the CGI can return special
** headers such as "Status:" and "Location:" which change the return
** status of the response. Since the return status has to be the very
** first line written out, we have to accumulate all the headers
** and check for the special ones before writing the status. Then
** we write out the saved headers and proceed to echo the rest of
** the response.
*/
size_t headers_size, headers_len;
char* headers;
char* br;
int status;
char* title;
char* cp;
/* Slurp in all headers. */
headers_size = 0;
add_to_buf( &headers, &headers_size, &headers_len, (char*) 0, 0 );
for (;;)
{
r = read( rfd, buf, sizeof(buf) );
if ( r <= 0 )
{
br = &(headers[headers_len]);
break;
}
add_to_buf( &headers, &headers_size, &headers_len, buf, r );
if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 ||
( br = strstr( headers, "\012\012" ) ) != (char*) 0 )
break;
}
/* If there were no headers, bail. */
if ( headers[0] == '\0' )
return;
/* Figure out the status. */
status = 200;
if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
cp < br &&
( cp == headers || *(cp-1) == '\012' ) )
{
cp += 7;
cp += strspn( cp, " \t" );
status = atoi( cp );
}
if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
cp < br &&
( cp == headers || *(cp-1) == '\012' ) )
status = 302;
/* Write the status line. */
switch ( status )
{
case 200: title = "OK"; break;
case 302: title = "Found"; break;
case 304: title = "Not Modified"; break;
case 400: title = "Bad Request"; break;
case 401: title = "Unauthorized"; break;
case 403: title = "Forbidden"; break;
case 404: title = "Not Found"; break;
case 408: title = "Request Timeout"; break;
case 500: title = "Internal Error"; break;
case 501: title = "Not Implemented"; break;
case 503: title = "Service Temporarily Overloaded"; break;
default: title = "Something"; break;
}
(void) snprintf(
buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title );
//(void) my_write( buf, strlen( buf ) );
//websWrite(wp, buf);
websWriteBlock(wp, buf, strlen(buf));
/* Write the saved headers. */
//(void) my_write( headers, headers_len );
websWriteBlock(wp, headers, headers_len);
cgip->fplacemark += headers_len;
}
}