Web Server in C - having trouble writing back to client browser from server code
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Rep:
Web Server in C - having trouble writing back to client browser from server code
A web server in C - most of which is from an online course, except:
The functions I wrote in this code are: lookup(), load(), indexes() and parse(). Everything seems fine up until the code they wrote is called (the response() function that sends the file content allocated and loaded in the load() function is attempted to send to the client browser. There are no errors returned from write(), but zero bytes are written to the client browser from the server, yet you can see that they are allocated (realloc()) and can print them out in the load() function. I am including all the code given for context, but the only code I wrote was in 4 small functions here: load(), parse(), indexes() and lookup().
To run this code after compiling, I do:
./server public
The public directory contains a test directory with an index.html file in it.
After starting the server on my Suse Linux machine, I have my browser (currently
using Chrome) send this command to the server: http://localhost:8080/home/karen/dev...est/index.html
Code:
// limits on an HTTP request's size, based on Apache's
// http://httpd.apache.org/docs/2.2/mod/core.html
#define LimitRequestFields 50
#define LimitRequestFieldSize 4094
#define LimitRequestLine 8190
char pathChars[LimitRequestLine + 1];
char pathCopy[LimitRequestLine + 1];
// number of bytes for buffers
#define BYTES 512
int main(int argc, char* argv[])
{
// parse command-line arguments
// ensure port is a non-negative short and path to server's root is specified
//start server
start(port, argv[optind]);
// listen for SIGINT (aka control-c)
struct sigaction act;
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
// a message and its length
char* message = NULL;
size_t length = 0;
// path requested
char* path = NULL;
// accept connections one at a time
while (true)
{
// free last path, if any
if (path != NULL)
{
free(path);
path = NULL;
}
// free last message, if any
if (message != NULL)
{
free(message);
message = NULL;
}
length = 0;
// close last client's socket, if any
if (cfd != -1)
{
close(cfd);
cfd = -1;
}
// check for control-c
if (signaled)
{
stop();
}
// check whether client has connected
if (connected())
{
// check for request
if (request(&message, &length))
{
// extract message's request-line
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
const char* haystack = message;
const char* needle = strstr(haystack, "\r\n");
if (needle == NULL)
{
error(500);
continue;
}
char line[needle - haystack + 2 + 1];
strncpy(line, haystack, needle - haystack + 2);
line[needle - haystack + 2] = '\0';
// log request-line
printf("%s", line);
// parse request-line
char abs_path[LimitRequestLine + 1];
char query[LimitRequestLine + 1];
if (parse(line, abs_path, query))
{
// URL-decode absolute-path
char* p = urldecode(abs_path);
if (p == NULL)
{
error(500);
continue;
}
// resolve absolute-path to local path
path = malloc(strlen(root) + strlen(p) + 1);
if (path == NULL)
{
error(500);
continue;
}
strcpy(path, root);
strcat(path, p);
free(p);
// ensure path exists
if (access(path, F_OK) == -1)
{
error(404);
continue;
}
// if path to directory
struct stat sb;
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
{
// redirect from absolute-path to absolute-path/
if (abs_path[strlen(abs_path) - 1] != '/')
{
char uri[strlen(abs_path) + 1 + 1];
strcpy(uri, abs_path);
strcat(uri, "/");
redirect(uri);
continue;
}
// use path/index.php or path/index.html, if present, instead of directory's path
printf("calling indexes with path = %s\n", path);
char* index = indexes(path);
if (index != NULL)
{
free(path);
path = index;
}
// list contents of directory
else
{
printf("calling list with path = %s\n", path);
list(path);
continue;
}
printf("index = %s\n", index);
}
// look up MIME type for file at path
printf("calling lookup on path = %s\n", path);
const char* type = lookup(path);
if (type == NULL)
{
error(501);
continue;
}
printf("type = %s\n", type);
// interpret PHP script at path
if (strcasecmp("text/x-php", type) == 0)
{
printf("interpret being called with path = %s and query = %s\n", path, query);
interpret(path, query);
}
// transfer file at path
else
{
printf("transfer being called with path = %s and query = %s\n", path, type);
transfer(path, type);
}
}
}
}
}
}
bool connected(void)
{
struct sockaddr_in cli_addr;
memset(&cli_addr, 0, sizeof(cli_addr));
socklen_t cli_len = sizeof(cli_addr);
cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_len);
if (cfd == -1)
{
return false;
}
return true;
}
void error(unsigned short code)
{
// determine code's reason-phrase
const char* phrase = reason(code);
if (phrase == NULL)
{
return;
}
// template for response's content
char* template = "<html><head><title>%i %s</title></head><body><h1>%i %s</h1></body></html>";
// render template
char body[(strlen(template) - 2 - ((int) log10(code) + 1) - 2 + strlen(phrase)) * 2 + 1];
int length = sprintf(body, template, code, phrase, code, phrase);
if (length < 0)
{
body[0] = '\0';
length = 0;
}
// respond with error
char* headers = "Content-Type: text/html\r\n";
respond(code, headers, body, length);
}
void freedir(struct dirent** namelist, int n)
{
if (namelist != NULL)
{
for (int i = 0; i < n; i++)
{
free(namelist[i]);
}
free(namelist);
}
}
void handler(int signal)
{
// control-c
if (signal == SIGINT)
{
signaled = true;
}
}
/**
* Escapes string for HTML. Returns dynamically allocated memory for escaped
* string that must be deallocated by caller.
*/
char* htmlspecialchars(const char* s)
{
// ensure s is not NULL
if (s == NULL)
{
return NULL;
}
// allocate enough space for an unescaped copy of s
char* t = malloc(strlen(s) + 1);
if (t == NULL)
{
return NULL;
}
t[0] = '\0';
// iterate over characters in s, escaping as needed
for (int i = 0, old = strlen(s), new = old; i < old; i++)
{
// escape &
if (s[i] == '&')
{
const char* entity = "&";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape "
else if (s[i] == '"')
{
const char* entity = """;
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape '
else if (s[i] == '\'')
{
const char* entity = "'";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape <
else if (s[i] == '<')
{
const char* entity = "<";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape >
else if (s[i] == '>')
{
const char* entity = ">";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// don't escape
else
{
strncat(t, s + i, 1);
}
}
// escaped string
return t;
}
char* indexes(const char* path)
{
char *returnPath = NULL;
int pathLen = 0;
int len = 0;
// TODO
pathLen = strlen(path);
if (strstr(path, "index.php"))
{
len = pathLen + 9 + 2; //add space for "/index.php" plus null
returnPath = (char *) calloc(1, len);
strncpy(returnPath, path, pathLen); //copy initial path to returnPath
strcat(returnPath, "/index.php");
}
else if (strstr(path, "index.html"))
{
len = pathLen + 10 + 2; //add space for "/index.html" plus null
returnPath = (char *) calloc(1, len);
strncpy(returnPath, path, pathLen); //copy initial path to returnPath
strcat(returnPath, "/index.html");
}
return returnPath;
}
void interpret(const char* path, const char* query)
{
// ensure path is readable
if (access(path, R_OK) == -1)
{
error(403);
return;
}
// open pipe to PHP interpreter
char* format = "QUERY_STRING="%s" REDIRECT_STATUS=200 SCRIPT_FILENAME="%s" php-cgi";
char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];
if (sprintf(command, format, query, path) < 0)
{
error(500);
return;
}
FILE* file = popen(command, "r");
if (file == NULL)
{
error(500);
return;
}
// load interpreter's content
char* content;
size_t length;
if (load(file, &content, &length) == false)
{
error(500);
return;
}
// close pipe
pclose(file);
// subtract php-cgi's headers from content's length to get body's length
char* haystack = content;
char* needle = strstr(haystack, "\r\n\r\n");
if (needle == NULL)
{
free(content);
error(500);
return;
}
// extract headers
char headers[needle + 2 - haystack + 1];
strncpy(headers, content, needle + 2 - haystack);
headers[needle + 2 - haystack] = '\0';
// respond with interpreter's content
respond(200, headers, needle + 4, length - (needle - haystack + 4));
// free interpreter's content
free(content);
}
void list(const char* path)
{
// ensure path is readable and executable
if (access(path, R_OK | X_OK) == -1)
{
error(403);
return;
}
// open directory
DIR* dir = opendir(path);
if (dir == NULL)
{
return;
}
// buffer for list items
char* list = malloc(1);
list[0] = '\0';
// iterate over directory entries
struct dirent** namelist = NULL;
int n = scandir(path, &namelist, NULL, alphasort);
for (int i = 0; i < n; i++)
{
// omit . from list
if (strcmp(namelist[i]->d_name, ".") == 0)
{
continue;
}
// escape entry's name
char* name = htmlspecialchars(namelist[i]->d_name);
if (name == NULL)
{
free(list);
freedir(namelist, n);
error(500);
return;
}
// append list item to buffer
char* template = "<li><a href="%s">%s</a></li>";
list = realloc(list, strlen(list) + strlen(template) - 2 + strlen(name) - 2 + strlen(name) + 1);
if (list == NULL)
{
free(name);
freedir(namelist, n);
error(500);
return;
}
if (sprintf(list + strlen(list), template, name, name) < 0)
{
free(name);
freedir(namelist, n);
free(list);
error(500);
return;
}
// free escaped name
free(name);
}
// free memory allocated by scandir
freedir(namelist, n);
// prepare response
const char* relative = path + strlen(root);
char* template = "<html><head><title>%s</title></head><body><h1>%s</h1><ul>%s</ul></body></html>";
char body[strlen(template) - 2 + strlen(relative) - 2 + strlen(relative) - 2 + strlen(list) + 1];
int length = sprintf(body, template, relative, relative, list);
if (length < 0)
{
free(list);
closedir(dir);
error(500);
return;
}
// free buffer
free(list);
// close directory
closedir(dir);
// respond with list
char* headers = "Content-Type: text/html\r\n";
respond(200, headers, body, length);
}
bool load(FILE* file, BYTE** content, size_t* length)
{
long filesize = 0;
long numBuffers = 0;
long numBytesToRead = 0;
bool returnValue = false;
long numBytesRead = 0;
long numBytesLastBuf = 0;
long saveNumBufs = 0;
BYTE *contentPtr = NULL;
// TODO
*length = 0;
if (file != NULL)
{
fseek (file , 0 , SEEK_END);
filesize = ftell (file);
rewind (file);
if (filesize > READMEGABYTEFROMFILE)
{
numBuffers = filesize / READMEGABYTEFROMFILE;
numBytesToRead = READMEGABYTEFROMFILE;
if(filesize%READMEGABYTEFROMFILE > 0)
{
numBuffers++;
numBytesLastBuf = filesize%READMEGABYTEFROMFILE;
}
}
else
{
numBuffers = 1;
numBytesToRead = filesize;
}
saveNumBufs = numBuffers;
while (numBuffers > 0)
{
if ((numBuffers == 1) && (saveNumBufs > 1))
{
numBytesToRead = numBytesLastBuf;
}
*content = realloc(*content, numBytesToRead);
if (*content == NULL)
{
returnValue = false;
break;
}
else
returnValue = true;
numBytesRead = fread (*content,1, numBytesToRead,file);
contentPtr = *content;
if ( (numBytesRead == 0) || (numBytesRead < numBytesToRead) )
{
if (ferror(file))
{
returnValue = false;
break;
}
if (feof(file))
{
returnValue = true;
}
}
*length += numBytesRead;
numBuffers--;
}
}
else
{
returnValue = false;
}
return returnValue;
}
const char* lookup(const char* path)
{
char *pathToPeriod;
char *lastPeriodInPath;
// TODO
strcpy(pathCopy, path);
pathToPeriod = pathCopy;
pathToPeriod = strtok(pathToPeriod, ".");
while(pathToPeriod != NULL)
{
pathToPeriod = strtok(NULL, ".");
if (pathToPeriod != NULL)
lastPeriodInPath = pathToPeriod;
}
if (!strcasecmp(lastPeriodInPath, "css"))
return "text/css";
else if (!strcasecmp(lastPeriodInPath, "html"))
return "text/html";
else if (!strcasecmp(lastPeriodInPath, "gif"))
return "image/gif";
else if (!strcasecmp(lastPeriodInPath, "ico"))
return "image/x-icon";
else if (!strcasecmp(lastPeriodInPath, "jpg"))
return "image/jpeg";
else if (!strcasecmp(lastPeriodInPath, "js"))
return "text/javascript";
else if (!strcasecmp(lastPeriodInPath, "php"))
return "text/x-php";
else if (!strcasecmp(lastPeriodInPath, "png"))
return "image/png";
return NULL;
}
bool parse(const char* line, char* abs_path, char* query)
{
// TODO
char *queryPtr = NULL;
int countAbsPath = 0;
int countQuery = 0;
char *ptrMethod = NULL;
char *ptrReqTarget = NULL;
char *ptrDblQuoteInReqTarget = NULL;
char *ptrHTTPversion = NULL;
char *ptrVersion = NULL;
char *ptrIsThereAqForQuery = NULL;
int len;
ptrMethod = strstr(line, "GET");
if (ptrMethod == NULL)
{
error(405);
return false;
}
while (*ptrMethod != ' ') //point to space past GET command
ptrMethod++;
ptrMethod++; //should now be pointing to request-target
ptrReqTarget = ptrMethod;
if (*ptrReqTarget != '/')
{
error(501);
return false;
}
else
countAbsPath++;
ptrReqTarget++;
while (*ptrReqTarget != ' ')
{
ptrReqTarget++;
countAbsPath++;
}
strcpy(pathChars, ptrMethod);
pathChars[countAbsPath] = NULL;
ptrMethod = &pathChars[0];
strcpy(abs_path, pathChars);
ptrIsThereAqForQuery = ptrReqTarget;
if (strstr(line, "?q"))
{
while (*ptrIsThereAqForQuery != '?') //point to end start of query
{
ptrIsThereAqForQuery++;
}
ptrIsThereAqForQuery += 3; //should now be pointing just past "?q=" and at start of query string
ptrDblQuoteInReqTarget = strchr(line, '"');
if (ptrDblQuoteInReqTarget != NULL)
{
error(400);
return false;
}
queryPtr = ptrIsThereAqForQuery;
while (*ptrIsThereAqForQuery != ' ') //point to end of query
{
ptrIsThereAqForQuery++;
countQuery++;
}
strncpy(query, queryPtr, countQuery);
query[countQuery + 1] = NULL;
}
else //no query found
{ //even if there is a ?, query = NULL
query = "";
}
ptrIsThereAqForQuery++;
ptrHTTPversion = ptrIsThereAqForQuery;
if (strncmp(ptrHTTPversion, "HTTP/1.1", 8))
{
error(505);
return false;
}
else
printf("HTTP/1.1 found - correct version for now\n");
return true;
}
/**
* Returns status code's reason phrase.
*
* http://www.w3.org/Protocols/rfc2616/...sec6.html#sec6
* https://tools.ietf.org/html/rfc2324
*/
const char* reason(unsigned short code)
{
switch (code)
{
case 200: return "OK";
case 301: return "Moved Permanently";
case 400: return "Bad Request";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 414: return "Request-URI Too Long";
case 418: return "I'm a teapot";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 505: return "HTTP Version Not Supported";
default: return NULL;
}
}
/**
* Redirects client to uri.
*/
void redirect(const char* uri)
{
char* template = "Location: %s\r\n";
char headers[strlen(template) - 2 + strlen(uri) + 1];
if (sprintf(headers, template, uri) < 0)
{
error(500);
return;
}
respond(301, headers, NULL, 0);
}
/**
* Reads (without blocking) an HTTP request's headers into memory dynamically allocated on heap.
* Stores address thereof in *message and length thereof in *length.
*/
bool request(char** message, size_t* length)
{
// ensure socket is open
if (cfd == -1)
{
return false;
}
// initialize message and its length
*message = NULL;
*length = 0;
printf("request: reading message\n");
// read message
while (*length < LimitRequestLine + LimitRequestFields * LimitRequestFieldSize + 4)
{
// read from socket
BYTE buffer[BYTES];
ssize_t bytes = read(cfd, buffer, BYTES);
if (bytes < 0)
{
if (*message != NULL)
{
free(*message);
*message = NULL;
}
*length = 0;
break;
}
// append bytes to message
*message = realloc(*message, *length + bytes + 1);
if (*message == NULL)
{
*length = 0;
break;
}
memcpy(*message + *length, buffer, bytes);
*length += bytes;
// null-terminate message thus far
*(*message + *length) = '\0';
// search for CRLF CRLF
int offset = (*length - bytes < 3) ? *length - bytes : 3;
char* haystack = *message + *length - bytes - offset;
char* needle = strstr(haystack, "\r\n\r\n");
if (needle != NULL)
{
// trim to one CRLF and null-terminate
*length = needle - *message + 2;
*message = realloc(*message, *length + 1);
if (*message == NULL)
{
break;
}
*(*message + *length) = '\0';
// ensure request-line is no longer than LimitRequestLine
haystack = *message;
needle = strstr(haystack, "\r\n");
if (needle == NULL || (needle - haystack + 2) > LimitRequestLine)
{
break;
}
// count fields in message
int fields = 0;
haystack = needle + 2;
while (*haystack != '\0')
{
// look for CRLF
needle = strstr(haystack, "\r\n");
if (needle == NULL)
{
break;
}
// ensure field is no longer than LimitRequestFieldSize
if (needle - haystack + 2 > LimitRequestFieldSize)
{
break;
}
// look beyond CRLF
haystack = needle + 2;
}
// if we didn't get to end of message, we must have erred
if (*haystack != '\0')
{
break;
}
// ensure message has no more than LimitRequestFields
if (fields > LimitRequestFields)
{
break;
}
// valid
return true;
}
}
// invalid
if (*message != NULL)
{
free(*message);
}
*message = NULL;
*length = 0;
return false;
}
/**
* Responds to a client with status code, headers, and body of specified length.
*/
void respond(int code, const char* headers, const char* body, size_t length)
{
unsigned long numBytesWrittenToClient = 0;
unsigned char *bodyPtr = &body[0];
// determine Status-Line's phrase
// http://www.w3.org/Protocols/rfc2616/...c6.html#sec6.1
const char* phrase = reason(code);
if (phrase == NULL)
{
return;
}
// respond with Status-Line
if (numBytesWrittenToClient = dprintf(cfd, "HTTP/1.1 %i %s\r\n", code, phrase) < 0)
{
return;
}
else
printf("num bytes written to client browser = %d\n", numBytesWrittenToClient);
// respond with headers
if (numBytesWrittenToClient = dprintf(cfd, "%s", headers) < 0)
{
printf("return from dprintf() less than zero--returning from respond()\n");
return;
}
else
printf("num bytes written to client browser = %d\n", numBytesWrittenToClient);
// respond with CRLF
if (numBytesWrittenToClient = dprintf(cfd, "\r\n") < 0)
{
return;
}
else
printf("num bytes written to client browser = %d\n", numBytesWrittenToClient);
// respond with body
if (numBytesWrittenToClient = write(cfd, body, length) == -1)
{
return;
}
else
printf("num bytes written to client browser = %d\n", numBytesWrittenToClient);
// log response line
if (code == 200)
{
// green
printf("\033[32m");
}
else
{
// red
printf("\033[33m");
}
printf("HTTP/1.1 %i %s", code, phrase);
printf("\033[39m\n");
}
/**
* Starts server on specified port rooted at path.
*/
void start(short port, const char* path)
{
// path to server's root
root = realpath(path, NULL);
if (root == NULL)
{
stop();
}
// ensure root is executable
if (access(root, X_OK) == -1)
{
stop();
}
// announce root
printf("\033[33m");
printf("Using %s for server's root", root);
printf("\033[39m\n");
// create a socket
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1)
{
stop();
}
// allow reuse of address (to avoid "Address already in use")
int optval = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
// assign name to socket
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
{
printf("\033[33m");
printf("Port %i already in use", port);
printf("\033[39m\n");
stop();
}
// listen for connections
if (listen(sfd, SOMAXCONN) == -1)
{
stop();
}
// announce port in use
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(sfd, (struct sockaddr*) &addr, &addrlen) == -1)
{
stop();
}
printf("\033[33m");
printf("Listening on port %i", ntohs(addr.sin_port));
printf("\033[39m\n");
}
/**
* Stop server, deallocating any resources.
*/
void stop(void)
{
// preserve errno across this function's library calls
int errsv = errno;
// announce stop
printf("\033[33m");
printf("Stopping server\n");
printf("\033[39m");
// free root, which was allocated by realpath
if (root != NULL)
{
free(root);
}
// close server socket
if (sfd != -1)
{
close(sfd);
}
// stop server
exit(errsv);
}
/**
* Transfers file at path with specified type to client.
*/
void transfer(const char* path, const char* type)
{
BYTE *contentPtr;
// ensure path is readable
if (access(path, R_OK) == -1)
{
error(403);
return;
}
// open file
FILE* file = fopen(path, "r");
if (file == NULL)
{
error(500);
return;
}
// load file's content
BYTE* content = NULL;
size_t length;
if (load(file, &content, &length) == false)
{
error(500);
return;
}
// close file
fclose(file);
// prepare response
char* template = "Content-Type: %s\r\n";
char headers[strlen(template) - 2 + strlen(type) + 1];
if (sprintf(headers, template, type) < 0)
{
error(500);
return;
}
// respond with file's content
respond(200, headers, content, length);
// free file's content
free(content);
}
/**
* URL-decodes string, returning dynamically allocated memory for decoded string
* that must be deallocated by caller.
*/
char* urldecode(const char* s)
{
// check whether s is NULL
if (s == NULL)
{
return NULL;
}
// allocate enough (zeroed) memory for an undecoded copy of s
char* t = calloc(strlen(s) + 1, 1);
if (t == NULL)
{
return NULL;
}
// iterate over characters in s, decoding percent-encoded octets, per
// https://www.ietf.org/rfc/rfc3986.txt
for (int i = 0, j = 0, n = strlen(s); i < n; i++, j++)
{
if (s[i] == '%' && i < n - 2)
{
char octet[3];
octet[0] = s[i + 1];
octet[1] = s[i + 2];
octet[2] = '\0';
t[j] = (char) strtol(octet, NULL, 16);
i += 2;
}
else if (s[i] == '+')
{
t[j] = ' ';
}
else
{
t[j] = s[i];
}
}
// escaped string
return t;
}
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
Yes, someone else had mentioned that. I removed the includes because otherwise the code exceeded the limit allowed to post here. I may re-post at a later date if I do not find the error myself before then, when I remove enough comments so that the includes will then fit in. Thank you for your response.
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
Yes, I understand. I have a phone interview I'm prepping for today and later I'll be back to working on my skills again.
I'll see if I can reduce the code size and post a version which compiles if I have not yet found the answer myself. Thanks!
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
Here are the includes, definitions, prototypes, etc, that were not included with the code above.
I think this is all I removed besides the comments to make it fit.
if (numBytesWrittenToClient = dprintf(cfd, "\r\n") < 0)
To actually get the numbytesWrittenToClient correct you need
Code:
if ((numBytesWrittenToClient = dprintf(cfd, "\r\n")) < 0)
This is due to the precedence of the "<" operator. What you actually got was
'numbBytesWrittenToClient = (dprintf(cfd, "\r\n") < 0)' which would set the count to either 1 or 0.
BTW, this error occurs in other places too. You might check anywhere you do this type of construct. It is an easy slip.
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
Sorry - forgot about that include file that I added - all it has in it are these few lines,
so if you comment that include out and add these few defines, that should work.
What unit testing or debugging have you tried on your own? Load it in a debugger, connect to it with telnet, and trace through it to see where it fails.
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
I added the parantheses where you mentioned - it still writes zero bytes to the client browser - not sure why yet.
This is output (printf's) that I made in my version (not included in the one I posted since it was too much).
karen pset6 $ ./server public
Using /home/karen/dev/webServerInC/pset6/public for server's root
Listening on port 8080
checking for client connections
starting infinite loop to look for client browser requests
checking if client has connected
calling accept(): extracts the first connection request on the queue of pending connections for the listening socket (server), sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket (client)
client: addr = 16777343
client has connected - check for request
request: reading message
message from client = GET /home/karen/webServerInC/pset6/public/test/index.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
length = 417
GET /home/karen/webServerInC/pset6/public/test/index.html HTTP/1.1
parsing request-line from client: line = GET /home/karen/webServerInC/pset6/public/test/index.html HTTP/1.1
parsing line = GET /home/karen/webServerInC/pset6/public/test/index.html HTTP/1.1
parsing for the GET method
should be pointing to request-target part of line = /home/karen/webServerInC/pset6/public/test/index.html HTTP/1.1
start of abs_path = /
pathChars = /home/karen/webServerInC/pset6/public/test/index.html countAbsPath = 53
len of abs_path = 0 len of pathChars = 53
parse: abs_path = /home/karen/webServerInC/pset6/public/test/index.html
no query found
parse: query =
are we pointing at HTTP's and it's version? HTTP/1.1
and HTTP/1.1
HTTP/1.1 found - correct version for now
abs_path returned from parse = /home/karen/webServerInC/pset6/public/test/index.html
query returned from parse =
respond(): responding to client browser with status code, headers, and body of specified length.
printing body bytes which in the load routine were the content bytes passed to this respond() function
0x3c 0x68 0x74 0x6d 0x6c 0x3e 0x3c 0x68 0x65 0x61 0x64 0x3e 0x3c 0x74 0x69 0x74 0x6c 0x65 0x3e 0x34 0x30 0x34 0x20 0x4e 0x6f 0x74 0x20 0x46 0x6f 0x75 0x6e 0x64 0x3c 0x2f 0x74 0x69 0x74 0x6c 0x65 0x3e 0x3c 0x2f 0x68 0x65 0x61 0x64 0x3e 0x3c 0x62 0x6f 0x64 0x79 0x3e 0x3c 0x68 0x31 0x3e 0x34 0x30 0x34 0x20 0x4e 0x6f 0x74 0x20 0x46 0x6f 0x75 0x6e 0x64 0x3c 0x2f 0x68 0x31 0x3e 0x3c 0x2f 0x62 0x6f 0x64 0x79 0x3e 0x3c 0x2f 0x68 0x74 0x6d 0x6c 0x3e
done printing content bytes
code = 404 phrase = reason(code) = Not Found
responding with status line to client
num bytes written to client browser = 0
calling dprintf() to print to client browser's socket file descriptor 4 with headers = Content-Type: text/html
num bytes written to client browser = 0
calling dprintf() to print to client browser's socket file descriptor with backslash-r backslash-n
num bytes written to client browser = 0
calling write() to client browser's socket file descriptor with body = <html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html> length = 89
num bytes written to client browser = 0
code NOT= 200, red
HTTP/1.1 404 Not Found
starting infinite loop to look for client browser requests
checking if client has connected
calling accept(): extracts the first connection request on the queue of pending connections for the listening socket (server), sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket (client)
client: addr = 16777343
client has connected - check for request
request: reading message
Distribution: currently Ubuntu Linux on my netbook while job searching, but also use Cygwin GNU tools on MSXP nb
Posts: 33
Original Poster
Rep:
I will check out the value of cfd most likely tomorrow since I do not have time today.
I just looked at the man page for netstat, but do not really know what you mean I should do to
see if my program has the socket open and is still listening. Do you do that from the command
line, or do you put that in the code? It looks like a command line thing you run but I was not
sure what to look for if I used it. I can do a web search but not until tomorrow! Thanks for
your response.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.