0

My first two functions passed the check50, but the last two report not standard errors. Am I on the right track? Could anyone give me some hints? Thank you!

load()

bool load(FILE* file, BYTE** content, size_t* length)
{
    // TODO
    BYTE* cursor;
    *content = realloc(*content, 1);
    if (*content == NULL)
    {
        return false;
    }
    cursor = *content;

    *length = 0;
    while (fread(cursor, 1, 1, file) == 1)
    {
        (*length)++;
        *content = realloc(*content, (*length) + 1);
        cursor = *content + *length;
        if (cursor == NULL)
        {
            return false;
        }
    }

    return true;
}

indexes()

char* indexes(const char* path)
{
    // TODO
    const char* php = "index.php";
    const char* html = "index.html";
    char* full_path = calloc(1, strlen(path) + strlen(php) + 1);
    if (full_path == NULL)
    {
        return NULL;
    }
    full_path = strcpy(full_path, path);
    full_path = strcat(full_path, php);
    if (access(full_path, F_OK) == 0)
    {
        return full_path;
    }
    else
    {
        free(full_path);
        full_path = calloc(1, strlen(path) + strlen(html) + 1);
        full_path = strcpy(full_path, path);
        full_path = strcat(full_path, html);
        if (access(full_path, F_OK) == 0)
        {
            return full_path;
        }
    }


    return NULL;
}

Update: add parse()

parse()

bool parse(const char* line, char* abs_path, char* query)
{
    // Check if the line contains only 2 spaces and end with CRLF.
    const char* cursor = line;
    int sp_count = 0;
    for (; *cursor != '\0'; cursor++)
    {
        if (*cursor == ' ')
        {
            sp_count++;
        }
    }
    if (sp_count != 2 || *(cursor - 2) != '\r' || *(cursor - 1) != '\n')
    {
        error(400);
        return false;
    }

    // if method is not GET, respond to the browser with 405 Method Not Allowed and return false
    if (strncmp(line, "GET ", 4) != 0)
    {
        error(405);
        return false;
    }
    // if request-target does not begin with /, respond to the browser with 501 Not Implemented and return false
    cursor = strchr(line, ' ') + 1;
    if (strncmp(cursor, "/", 1) != 0)
    {
        error(501);
        return false;
    }
    // if request-target contains a ", respond to the browser with 400 Bad Request and return false
    cursor = strchr(line, ' ') + 1;
    for (; *cursor != ' '; cursor++)
    {
        if (*cursor == '"')
        {
            error(400);
            return false;
        }
    }
    // if HTTP-version is not HTTP/1.1, respond to the browser with 505 HTTP Version Not Supported and return false
    cursor = strrchr(line, ' ') + 1;
    if (strncmp(cursor, "HTTP/1.1", 8) != 0)
    {
        error(505);
        return false;
    }

    // If all checks passed, divide and store request-target into abs_path and query
    char* cursor_r = strchr(line, ' ') + 1;
    char* cursor_w = abs_path;
    int question_mark_counter = 0;
    for (; *cursor_r != ' '; cursor_r++, cursor_w++)
    {
        if (*cursor_r == '?')
        {
            question_mark_counter++;
            *cursor_w = '\0';// end the abs_path first
            // if characters follow '?'
            if (*(cursor_r + 1) != ' ') 
            {
                cursor_w = query;// prepare to write in query
                cursor_r++;// ignore the '?'                
            }
            // if no characters follow '?', fill the query with '\0'
            else
            {
                cursor_w = query;
                *cursor_w = '\0';
                return true;
            }

        }

        *cursor_w = *cursor_r;
    }
    // if no '?', end abs_path and query
    if (question_mark_counter == 0)
    {
        *cursor_w = '\0';
        cursor_w = query;
        *cursor_w = '\0';
        return true;
    }

    return false;
}

Update: debugging screenshots

What does the report mean? And what am I expected to do next? I think CS50 should add great more details to debugging with gdb with more complex files.

enter image description here enter image description here

Check50

:) server.c exists
:) server compiles
:) Requesting cat.jpg returns 200, image/jpeg, and correct image
:) Requesting cat.html returns 200, text/html, and correct file
:) Requesting cat2.HTML returns 200, text/html, and correct file
:) Requesting cat3.HtMl returns 200, text/html, and correct file
:) Requesting cat.gif returns 200, image/gif, and correct file
:) Requesting favicon.ico returns 200, image/x-icon, and correct file
:( Requesting test.css returns 200, text/css, and correct file
   \ expected an exit code of 0, not standard error of "======= Backtrace: =========\n======= M..."
:( Requesting test.js returns 200, text/javascript, and correct file
   \ expected an exit code of 0, not standard error of "======= Backtrace: =========\n======= M..."
:( Requesting hello.php returns 200, text/html, and correct output
   \ expected output, not standard error of "======= Backtrace: =========\n======= M..."
:( Requesting hello.php? returns 200, text/html, and correct output
   \ expected output, not standard error of "======= Backtrace: =========\n======= M..."
:( Requesting hello.php?name=Alice returns 200, text/html, and correct output
   \ expected output, not an exit code of 0
:) Requesting /test redirects to /test/
:( Requesting /test/ outputs /test/index.html
:( Requesting directory containing index.php outputs index.php
   \ expected output, not standard error of "======= Backtrace: =========\n======= M..."
:( Requesting two files in a row (cat.html then cat.jpg) succeeds
-1

analysis after OP edits

  • the seg fault in gdb is telling you that the function is failing on a realloc. It seems likely it is the realloc in load. To be sure, you should n through the load function one line at a time, and not just continue. Then you will know precisely where the seg fault occurs.
  • If a curl request to hello.html is failing on the realloc in load, how could cat.jpg et.al. have passed check50? Why didn't they seg fault too? Since check50 is a black-box, there is no way of knowing what exactly it does. But server is an open book; we know that hello.html does parse -> lookup -> load. One conclusion is that the realloc is failing in load because of a memory leak in another function. Does cat.jpg seg fault when you request it in a browser?

  • There is a memory leak in indexes which would be causing some problems. This allocation char* full_path = calloc(1, strlen(path) + strlen(php)); does not accommodate the null terminator. (+1). But hello.html does not "go through" indexes, so that is not causing this seg fault. (It does need to be fixed, however).

  • There is more than likely a memory leak in lookup. The "test.css", "test.js" and "hello.php" failures (seg faults) should direct your attention to the lookup function in any case. Notice, check50/server1 only superficially "tested" lookup, with the "cat.exe" test.

  • The two "php" failures should direct your attention to parse and how query is built. Since they seg-fault, and parse is called before lookup, there's likely a problem in parse too. check50/server1 (black-box!) does very specific testing for the specified parse conditions. You might notice it does not do any test involving query.

This pset really challenges one to exhibit a solid understanding of pointers and memory allocation and learning to use new functions; coalescing everything learned thus far. It can never be said that it's easy. One of the tougher parts of this assignment is the fact that each function may work fine on it's own, but may not "play nice" together.

valgrind is the go to tool for hunting memory leaks. This call to valgrind valgrind -v --leak-check=full --show-leak-kinds=all --track-origins=yes will maximize feedback. Remember, valgrind only reports on the data it processes. So calling server under valgrind, and then ctrl-c to end it, will generally be "clean" because it has not processed a request. Using a similar strategy to gdb, call valgrind in one terminal, send a curl request in another terminal, then terminate valgrind to see the results.

Of course, jumping right into valgrind at this point is bound to add to confusion initially. I suggest you read the program and look for memory leaks. The off-by-one errors (ie not allocating a char* to accommodate the null terminator) are usually the easiest to find and fix.

And it's always a good idea to test server as extensively as possible with browser and curl requests before submitting to check50.


original post analysis
Such "catastrophic" check50 failure generally indicates load. This \ expected an exit code of 0, not standard error of "======= Backtrace: =========\n======= indicates a seg fault.

One problem in load is that *length is never initialized. So it has the value of whatever is in memory at that location. Take server for a spin in debug50 (you'll find intro to debug50 here) or gdb (tips here), with a break set at load, and another break set after you increment *length, to see what the computer thinks. It might be best to debug with a small text file and a curl request, something like curl -i http://localhost:8080/hello.html.

Another problem is here while (fread(cursor, 1, 1, file) == 1). Program is reading every byte into the first byte of cursor. So I'd expect cursor and *content (since they are pointing to the same memory location as per cursor = *content;) to be whatever is in the last byte of file.

The first line of hello.html is "<!DOCTYPE html>". Let's look at the first few iterations of the while loop:

iter 1: read byte 1 of hello.html into cursor. Now cursor is "<"
iter 2: read byte 2 of hello.html into cursor. Now cursor is "!"
iter 3: read byte 3 of hello.html into cursor. Now cursor is "D"
iter 4: read byte 4 of hello.html into cursor. Now cursor is "O"

&tc.

Here is a troubleshooting plan for gdb to get you started.

  1. Open server.c source in a tab so you can view the source code when needed.
  2. Open a terminal. Start server under gdb, as with gdb ./server
  3. Set a breakpoint at load as with (gdb) break load
  4. Run server with public as root directory as with (gdb) run ./public
  5. Now server is running under gdb in that terminal. It is waiting for requests.
  6. In another terminal, issue a curl request for hello.html, as with curl -i http://localhost:8080/hello.html
  7. Back in the gdb tab, program should be waiting at load.
  8. Set breakpoints within load (that's where the source comes in handy). I suggest a breakpoint at this line if (cursor == NULL). Inspect the contents of *length and cursor to see what the program is doing.

gdb should not be a painful experience. There's a learning curve, to be sure, but practice, patience and perseverance will prevail. I always suggest starting with a curl request so the calls to favicon.ico can be avoided. And remember, if nothing is happening in the curl terminal, it's waiting for something to happen in the gdb terminal. And versa vice.

There is a problem in indexes too. The common sizeof trap. You probably meant "length of" not "size of". (*path is a pointer, sizeof a pointer is 8). This post has an excellent explanation.

17
  • Thank you! path is a pointer, but doesn't *path mean go to what the pointer points to and get the content? – Kevin King Oct 12 '16 at 15:05
  • *content was initiated at the beginning when the first byte was allocated and never changed. Only the cursor was moving to the last byte of the file. How can *content point to the last byte of the file? – Kevin King Oct 12 '16 at 15:21
  • sizeof is wrong. See this post for an excellent explanation. fread is "moving to the last byte" but it is always storing the result in the first byte of cursor. – DinoCoderSaurus Oct 12 '16 at 16:21
  • But *content is not moved, pointing at the first byte of the memory, and is returned. Why would I care where cursor is? And after realloc, the cursor is pointing at the newly added memory, right? – Kevin King Oct 13 '16 at 7:19
  • How to use gdb to debug? Using gdb is really a painful experience. Please give more details. Thank you! – Kevin King Oct 13 '16 at 8:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .