This Is Not The Course Website You Are Looking For

This course website is from a previous semester. If you are currently in the class, please make sure you are viewing the latest course website instead of this old one.

The goal of this homework assignment is to allow you to practice communicating across the Internet via sockets and performing parallel computing with multiple processes in C. The first activity involves implementing a HTTP library similar to Python's Requests, while the second activity requires you to use this library to build a thor utility that resembles curl (except with the ability to make many requests using multiple processes).

For this assignment, record your source code and any responses to the following activities in the homework09 folder of your assignments GitHub repository and push your work by noon, Saturday, April 29.

Activity 0: Preparation

Before starting this homework assignment, you should first perform a git pull to retrieve any changes in your remote GitHub repository:

$ cd path/to/repository                   # Go to assignments repository

$ git checkout master                     # Make sure we are in master branch

$ git pull --rebase                       # Get any remote changes not present locally

Next, create a new branch for this assignment:

$ git checkout -b homework09              # Create homework09 branch and check it out

You are now ready to work on the activities below.

Frequently Asked Questions

Activity 1: HTTP Library (9 Points)

The goal of this assignment is for you translate the following thor.py Python script into an equivalent C program by using system calls such as socket and fork:

import concurrent.futures
import os
import sys
import time

import requests

# Functions

def usage(status=0):
    progname = os.path.basename(sys.argv[0])
    print(f'''Usage: {progname} [-n HAMMERS] URL
    -n HAMMERS  How many times to hammer the URL
    ''')
    sys.exit(status)

def hammer(url):
    ''' Download and display the contents of the given URL.

    Prints the bandwidth (MB/s) of the HTTP transaction.
    '''
    # Make HTTP Request
    start_time   = time.time()
    response     = requests.get(url, allow_redirects=False)
    result       = response.status_code == 200

    if result:
        # Compute elapsed time and bandwidth
        stop_time    = time.time()
        elapsed_time = stop_time - start_time
        bandwidth    = len(response.content) / (1<<20) / elapsed_time

        # Display HTTP body
        sys.stdout.write(response.content.decode())

        # Display Bandwidth (bytes / seconds)
        sys.stderr.write(f'Bandwidth: {bandwidth:0.2f} MB/s\n')

    # Return if HTTP Request was successful
    return result

def throw(url, hammers):
    start_time   = time.time()
    urls         = [url]*hammers

    # Throw all hammers concurrently using multiple processes
    with concurrent.futures.ProcessPoolExecutor(hammers) as executor:
        result   = all(executor.map(hammer, urls))

    stop_time    = time.time()
    elapsed_time = stop_time - start_time

    # Display elapsed time
    sys.stderr.write(f'Elapsed Time: {elapsed_time:0.2f} seconds\n')

    # Return if all HTTP Requests were successful
    return result

# Main Execution

def main():
    hammers   = 1
    arguments = sys.argv[1:]

    # Parse command line arguments
    while arguments and arguments[0].startswith('-'):
        argument = arguments.pop(0)
        if argument == '-n':
            hammers = int(arguments.pop(0))
        elif argument == '-h':
            usage(0)
        else:
            usage(1)

    if not arguments:
        usage(1)

    url = arguments.pop(0)
    if not url.startswith('http://'):
        url = 'http://' + url

    # Throw hammers at URL
    result = throw(url, hammers)
    sys.exit(not result)

if __name__ == '__main__':
    main()

To do so, you will first create a HTTP library called libthor.a which contains all the underlying functionality required to make HTTP requests and utilize multiple processes for concurrency.

As discussed in class, web clients such as your web browser utilize HTTP to request data from remote web servers. This communication involves the client using a TCP or streaming socket to connect to a server to send a request that looks something like this:

GET /path HTTP/1.0
Host: domain.com

The first line of the request specifies the operation (ie. GET) followed by the resource being request (ie. /PATH) and then the protocol spoken by the client (ie. HTTP/1.0). After this first line, comes a series of headers, one of which is the Host or the name of the machine we are requesting the resource from. To terminate the headers, the client writes a blank line.

DOS Line Endings

It turns out that the HTTP protocol uses our friend, DOS line endings. This means that each line in the HTTP request and response is terminated by \r\n or CRLF. Keep this in mind when writing your request and reading the response.

Once the request has been written to the server, the client then waits to read back the response, which looks something like this:

HTTP/1.1 200 OK
Content-Length: 1983

<html>
...
</html>

The first line of the response contains the status of the request. Successful requests have 200 OK while non-successful requests will have different status codes such as 301 Moved Permanently or 404 Not Found. After this first line, the response will contain a series of headers, one of which is the Content-Length that tells the client how large the contents of the response body should be. Following the headers is a blank line, and then the contents of the response. This is usually the file or resource that was requested (ie. HTML or image data).

Note: Not all HTTP servers will return a Content-Length, so keep that in mind when implementing your HTTP client.

The overall goal of this assignment is for you to build your own version curl that speaks basic HTTP using TCP sockets as described above. Additionally, your version of a HTTP client will support hammering the HTTP server by using multiple child processes to make concurrent HTTP requests.

The remainder of this document details how you are to first build a library that supports communicating via HTTP and then the thor utility that provides a command-line interface to the library's functionality.

Skeleton Code

To help you get started, the instructor has provided you with the following skeleton code:

# Go to assignments repository
$ cd path/to/assignments/repository

# ------------------------------------------------
# MAKE SURE YOU ARE NOT INSIDE homework09
# ------------------------------------------------
# MAKE SURE YOU ARE AT THE TOP-LEVEL DIRECTORY
# ------------------------------------------------

# Download skeleton code tarball
$ curl -LO https://raw.githubusercontent.com/nd-cse-20289-sp23/cse-20289-sp23-assignments/master/homework09/homework09.tar.gz

# Extract skeleton code tarball
$ tar xzvf homework09.tar.gz

Once downloaded and extracted, you should see the following files in your homework09 directory:

homework09
    \_ Makefile               # This is the Makefile for building all the project artifacts
    \_ README.md              # This is the README file for recording your responses
    \_ bin                    # This contains the binary executables and test scripts
      \_ loki.py              # This is the Python script that implements an HTTP server
      \_ test_hammer.sh       # This is the shell script for testing the hammer and throw functions
      \_ test_request.sh      # This is the shell script for testing the Request structure
      \_ test_socket.sh       # This is the shell script for testing the socket functions
      \_ test_thor.sh         # This is the shell script for testing the thor utility
      \_ test_timestamp.sh    # This is the shell script for testing the timestamp functions
    \_ include                # This contains the header files
      \_ hammer.h             # This is the C header file for hammer functions
      \_ macros.h             # This is the C header file for macros
      \_ request.h            # This is the C header file for Request structure
      \_ socket.h             # This is the C header file for socket functions
      \_ timestamp.h          # This is the C header file for timestamp functions
    \_ lib                    # This contains the library files
    \_ src                    # This contains the C implementation files
      \_ hammer.c             # This is the C implementation file for the hammer functions
      \_ request.c            # This is the C implementation file for the Request structure
      \_ socket.c             # This is the C implementation file for the socket functions
      \_ thor.c               # This is the C implementation file for the thor utility
      \_ timestamp.c          # This is the C implementation file for the timestamp functions
    \_ tests                  # This contains the unit test C implementation files
      \_ unit_hammer.c        # This is the C implementation file for the hammer unit test
      \_ unit_request.c       # This is the C implementation file for the Request unit test
      \_ unit_socket.c        # This is the C implementation file for the socket unit test
      \_ unit_timestamp.c     # This is the C implementation file for the timestamp unit test

The details on what you need to implement are described in the following sections.

Task 0: Headers

The following is a description of the header files included in the skeleton code.

include/request.h

The include/request.h file is the header file for the Request struct. In addition to the function prototypes, this header defines the following type:

typedef struct {
    char *host;     /* URL host */
    char *port;     /* URL port */
    char *path;     /* URL path */
} Request;

This defines a Request struct type consisting of three [string]s:

  1. host contains the host component of a URL.
  2. port contains the port component of a URL.
  3. path contains the path component of a URL.

As shown above, a URL may contain five different components: protocol, host, port, path, and query string. The URL struct only needs to store the host, port, and path of any URL provided by the user.

Missing URL Components

Note: Not all URLs will specify every component explicitly. Some may be missing the protocol or the port or even the path (the host must always be given).

For instance, here are some valid URLs you will need to support:

If the port is not specified, then you are to assume the default HTTP port: 80. If a path is not specified, then you can assume an empty path: "".

include/hammer.h, include/socket.h, include/timestamp.h

The include/hammer.h, include/socket.h, and include/timestamp.h header files contain function prototypes but do not introduce any new types or structs.

More details on how to implement the functions declared in these header files is provided below.

include/macros.h

Finally, the include/macros.h header file provides a debug macro that you can use to insert debugging statements into your code (rather than printf):

debug("foo = %d", foo);

The advantage of using this macro is that it displays the file name, line number, and function name where the command is called in its output, along with the formatted message you specified.

Likewise, the header file also provides a streq macro that you can use instead of strcmp:

if (streq(source, target)) {
    ...
}

For this task, you do not need to modify these header files. Instead, you should review them and ensure you understand the provided code.

Task 1: Makefile

Once again, the Makefile contains all the rules or recipes for building the project artifacts (e.g. libthor.a, thor, etc.). Although the provided Makefile contains most of the variable definitions and test recipes, you must add the appropriate rules for libthor.a, unit_hammer, unit_request, unit_socket, unit_timestamp, thor, and any intermediate objects. The dependencies for these targets are shown in the DAG below:

Makefile Variables

You must use the CC, CFLAGS, LD, LDFLAGS, AR, and ARFLAGS variables when appropriate in your rules.

Once you have a working Makefile, you should be able to run the following commands:

# Build all TARGETS
$ make
Compiling src/thor.o...
Compiling src/timestamp.o...
Compiling src/socket.o...
Compiling src/request.o...
Compiling src/hammer.o...
Linking lib/libthor.a...
Linking bin/thor...

# Run all tests
$ make test
Compiling tests/unit_timestamp.o...
Linking bin/unit_timestamp...
Testing timestamp...

# Simulate build with tracing output
$ make -n
echo Compiling src/thor.o...
gcc -g -Wall -std=gnu99 -Iinclude -c -o src/thor.o src/thor.c
echo Compiling src/timestamp.o...
gcc -g -Wall -std=gnu99 -Iinclude -c -o src/timestamp.o src/timestamp.c
echo Compiling src/socket.o...
gcc -g -Wall -std=gnu99 -Iinclude -c -o src/socket.o src/socket.c
echo Compiling src/request.o...
gcc -g -Wall -std=gnu99 -Iinclude -c -o src/request.o src/request.c
echo Compiling src/hammer.o...
gcc -g -Wall -std=gnu99 -Iinclude -c -o src/hammer.o src/hammer.c
echo Linking lib/libthor.a...
ar rcs lib/libthor.a src/timestamp.o src/socket.o src/request.o src/hammer.o
echo Linking bin/thor...
gcc -Llib -o bin/thor src/thor.o lib/libthor.a

Depending on your compiler, you may see some warnings with the initial skeleton code. Likewise, the test programs will all fail in some fashion.

Task 2: src/timestamp.c

The src/timestamp.c file contains the C implementation for time related functions. For this task, you will need to implement the following function:

  1. double timestamp()

    This function returns a double that corresponds to the current time in seconds.

    Note:

    • You must check if any of the system calls fail.

    • You must use gettimeofday to account for both seconds and microseconds using this formula:

      timestamp = seconds + (microseconds / 1000000.0).
      

As you implement src/timestamp.c, you can test it by running the test-timestamp target:

# Run test-timestamp target
$ make test-timestamp
Compiling tests/unit_timestamp.o...
Linking bin/unit_timestamp...
Testing timestamp...
 timestamp                                          ... Success

   Score 0.50 / 0.50
  Status Success

# Run unit_timestamp manually
$ bin/unit_timestamp 0

Task 3: src/socket.c

The src/socket.c file contains the C implementation for the socket functions. For this task, you will need to implement the following function:

  1. FILE *socket_dial(const char *host, const char *port)

    This function uses socket to connect to the given host and port using TCP and returns a read/writable FILE stream corresponding to the socket.

    Note:

As you implement src/socket.c, you can test it by running the test-socket target:

# Run test-socket target
$ make test-socket
Compiling tests/unit_socket.o...
Linking bin/unit_socket...
Testing socket...
 socket_dial_success                                ... Success
 socket_dial_failure                                ... Success
 socket_dial_mode                                   ... Success

   Score 1.00 / 1.00
  Status Success

# Run unit_socket manually
$ bin/unit_socket 0

Task 4: src/request.c

The src/request.c file contains the C implementation for the Request struct. For this task, you will need to implement the following functions:

  1. Request * request_create(const char *url)

    This function allocates a new Request struct, sets its host, port, and path fields based on the components in the given url, and returns the allocated struct.

    Note:

    • You must check if allocation fails.

    • You must allocate and copy host, port, and path (consider strdup).

    • You may wish to copy the url string to a local buffer that you can manipulate.

    • You may wish to use strstr and strchr to search the local buffer for the appropriate delimiters (e.g. HOST_DELIMITER, PATH_DELIMITER, and PORT_DELIMITER) and split the string into different components.

  2. void request_delete(Request *request)

    This function deallocates the given Request struct and any previously allocated fields such as host, port, and path.

  3. ssize_t request_stream(const Request *request, FILE *stream)

    This function makes a HTTP request with the given Request struct by using socket_dial and writes the response body to the given stream. It returns the number of bytes written to the output stream or -1 if any error was experienced during the HTTP request.

    Note:

    • You must check if any of the system calls fail.

    • You will want to follow the HTTP transaction steps described above and outlined below:

      1. Connect to remote host and port.
      2. Send HTTP request to remote server.
      3. Read response status and verify success.
      4. Read response headers (until there is an empty line).
      5. Read response body and copy to stream.
      6. Close connection.
      7. Return number of bytes written to stream.

    • Remember that HTTP uses DOS line endings and terminates each line with \r\n.

    • You may wish to use strstr to check the response status.

    • You may wish to use sscanf to parse the response headers for the value of Content-Length.

    • You may wish to use fgets to read the response status and headers.

    • You must use fread and fwrite to read the response body since it may contain binary data.

    • If the server provides a Content-Length and the number of bytes written to steam does not match, then the function should return -1 to indicate an error. Otherwise, if there is no Content-Length or the number of bytes written matches, then it should return the number of bytes written.

    • Only a HTTP status of 200 OK is considered successful.

As you implement src/request.c, you can test it by running the test-request target:

# Run test-request target
$ make test-request
Compiling tests/unit_request.o...
Linking bin/unit_request...
Testing request...
 request_create                                     ... Success
 request_delete                                     ... Success
 request_stream_success                             ... Success
 request_stream_failure                             ... Success

   Score 3.00 / 3.00
  Status Success

# Run unit_request manually
$ bin/unit_request 0

Task 5: src/hammer.c

The src/hammer.c file contains the C implementation for hammer and throw functions. For this task, you will need to implement the following functions:

  1. bool hammer(const URL *url, FILE *stream)

    This function performs a HTTP request by creating a Request struct and request_stream. It will measure the bandwidth by using the timestamp function to compute the total elapsed time of the HTTP request and then dividing it by the number of bytes written to the stream. It will return true if the HTTP request was successful.

    Note:

    • You must check if any of the system calls fail.

    • You must compute the bandwidth by using the following formula:

      bandwidth = bytes_written / 1048576.0 / (end_time - start_time)
      

      bytes_written will be returned from request_stream() while end_time and start_time can be retrieved from timestamp().

      You must print out the bandwidth to stderr in the following format: Bandwidth: %0.2lf MB/s.

  2. bool throw(const URL *url, size_t hammers, FILE *stream)

    This function uses fork to first create hammers number of processes where each child process performs an HTTP request using the hammer function on the given url and stream. After throwing all the hammers, the parent process must wait on each child process and displays the total elapsed time for the whole function. It will return true if all child processes reported success, otherwise false.

    Note:

    • You must check if any of the system calls fail.

    • The parent must fork all the child processes and then wait for them. It will need to use the timestamp() function to compute the overall elapsed time for the function.

    • You must print out the elapsed time to stderr in the following format: Elapsed Time: %0.2lf seconds.

As you implement src/hammer.c, you can test it by running the test-hammer target:

# Run test-hammer target
$ make test-hammer
Compiling tests/unit_hammer.o...
Linking bin/unit_hammer...
Testing hammer...
 hammer_success                                     ... Success
 hammer_failure                                     ... Success
 hammer_bandwidth                                   ... Success
 throw_success                                      ... Success
 throw_failure                                      ... Success
 throw_elapsed                                      ... Success
 throw_clones                                       ... Success

   Score 4.50 / 4.50
  Status Success

# Run unit_hammer manually
$ bin/unit_hammer 0

Activity 2: Thor (5 Points)

Once you have a working HTTP library, you are to complete the src/thor.c program:

# Display usage
$ ./bin/thor -h
Usage: ./bin/thor [options] URL
    -n HAMMERS    How many times to hammer the URL

The bin/thor program takes the given URL and makes N requests to it using multiple processes which write their responses to standard output. If N is not specified, then it defaults to 1.

Examples

Here are some examples of bin/thor in action:

# Throw one hammer at example.com
$ ./bin/thor example.com
<!doctype html>
    <title>Example Domain</title>
...
</body>
</html>
Bandwidth: 0.05 MB/s
Elapsed Time: 0.02 seconds

# Throw two hammers at example.com
$ ./bin/thor -n 2 example.com
<!doctype html>
    <title>Example Domain</title>
...
</body>
</body>
</html>
</html>
Bandwidth: 0.05 MB/s
Bandwidth: 0.05 MB/s
Elapsed Time: 0.02 seconds

Note: Each child process should print the bandwidth it computes to stderr, while the parent prints the elapsed time to stderr.

Task 1: src/thor.c

The src/thor.c file contains the C implementation of the thor tool described above.

To complete this program, you will need to do the following:

  1. Parse the command-line arguments.
  2. Hammer the given URL with throw().

As you implement src/thor.c, you can test it by running the test-thor target:

# Run test-thor target
$ make test-thor
Compiling src/thor.o...
Linking bin/thor...
Testing thor...
 thor                                               ... Success
 thor -h                                            ... Success
 thor -?                                            ... Success
 thor http://example.com                            ... Success
 thor -n 2 http://example.com                       ... Success
 thor http://nd.edu                                 ... Success
 thor -n 1 http://nd.edu                            ... Success
 thor h4x0r.space                                   ... Success
 thor -n 4 h4x0r.space                              ... Success
 thor h4x0r.space:9898/walden.txt                   ... Success
 thor -n 4 h4x0r.space:9898/walden.txt              ... Success
 thor h4x0r.space:9898/gatsby.txt                   ... Success
 thor -n 5 h4x0r.space:9898/gatsby.txt              ... Success
 thor http://h4x0r.space:9898/warandpeace.txt       ... Success
 thor -n 3 http://h4x0r.space:9898/warandpeace.txt  ... Success

   Score 5.00 / 5.00
  Status Success

# Run test_thor.sh manually
$ bin/test_thor.sh

Activity 3: Quiz (2 Points)

Once you have completed all the activities above, you are to complete the following reflection quiz:

As with Reading 01, you will need to store your answers in a homework09/answers.json file. You can use the form above to generate the contents of this file, or you can write the JSON by hand.

To test your quiz, you can use the check.py script:

$ ./check.py
Checking homework09 quiz ...
     Q01 0.40
     Q02 0.40
     Q03 0.30
     Q04 0.30
     Q05 0.30
     Q06 0.30
   Score 2.00 / 2.00
  Status Success

Guru Point: TROLL, IRC Bot, VPS (3 Points)

For this week, there are three Guru Point opportunities.

Self-Service Extension

Remember that you can always forgo these Guru Points for two extra days to do the homework. That is, if you need an extension, you can simply skip the Guru Points and you will automatically have until Monday to complete the assignment for full credit.

Just leave a note on your Pull Request of your intensions.

Note: For this week, we will waive forgoing the Guru Points in order to get two extra days to do the homework. This means, that you can take the self-service extension and still do all of the Guru Points.

TROLL (1 Point)

For extra credit, you are to use C and system calls to implement your own version of the TROLL from Homework 01. Recall, that the TROLL was a process that intercepted signals such as SIGINT and SIGTERM and taunted you when you tried to terminate it. Your version of the TROLL should do something similar (prevent easy termination)... but its taunts and other aesthetic details are up to you.

Verification

To get credit for this Guru Point, you must show either a TA a demonstration of your TROLL in action (or attach a video / screenshot to your Pull Request). You have up until Friday, May, 5 to verify your guru point.

IRC Bot (1 Point)

For extra credit, you are to use Python and sockets to implement your own version of bobbit, an IRC chat bot. Your bot should connect to the chat.ndlug.org server and join the #bots channel. It should be able to respond to at least one type of command or message. The particular operation is up to you.

To help you get started, here are some resources:

NDLUG IRC

To connect to the NDLUG server yourself, you can use the following anonymous web client:

https://gamja.ndlug.org/?channels=#bots

Alternatively, if you wish to have access to more chat features such as history, you can register via regserv.ndlug.org, which will setup a Lounge and IRC account for you.

The Lounge is a web-based IRC client that you can use from any web browser, but you are free to connect to the IRC server from any IRC client such as Weechat, Hexchat, or Textual using chat.ndlug.org as the hostname and 6697 as the port.

A basic IRC client session looks like this:

USER ircle-pbui 0 * :pbui's bot
NICK ircle-pbui
JOIN #bots
PRIVMSG #bots :I've fallen and I can't get up!
  1. The USER command sets the users real name and registers the user.

  2. The NICK command sets the users nickname.

  3. The JOIN command allows the user to join in a channel (in this case #bots).

  4. The PRIVMSG command allows the user to send a message (in this case to the channel #bots).

Here is a basic skeleton, ircle.py, that you can start with:

import os
import socket
import ssl

# Constants

HOST = 'chat.ndlug.org'
PORT = 6697
NICK = f'ircle-{os.environ["USER"]}'

# Functions

def ircle():
    # Connect to IRC server
    ssl_context = ssl.create_default_context()
    tcp_socket  = socket.create_connection((HOST, PORT))
    ssl_socket  = ssl_context.wrap_socket(tcp_socket, server_hostname=HOST)
    ssl_stream  = ssl_socket.makefile('rw')

    # Identify ourselves
    ssl_stream.write(f'USER {NICK} 0 * :{NICK}\r\n')
    ssl_stream.write(f'NICK {NICK}\r\n')
    ssl_stream.flush()

    # Join #bots channel
    ssl_stream.write(f'JOIN #bots\r\n')
    ssl_stream.flush()

    # Write message to channel
    ssl_stream.write(f"PRIVMSG #bots :I've fallen and I can't get up!\r\n")
    ssl_stream.flush()

    # Read and display
    while True:
        message = ssl_stream.readline().strip()
        print(message)

# Main Execution

def main():
    ircle()

if __name__ == '__main__':
    main()

Alternatively, if you are interested in using the new asyncio features of Python 3 to perform event-driven and concurrent programming, you can use the following skeleton ircle-async.py:

import asyncio
import os
import sys

# Constants

HOST = 'chat.ndlug.org'
PORT = 6697
NICK = f'ircle-{os.environ["USER"]}'

# Functions

async def ircle():
    # Connect to IRC server
    reader, writer = await asyncio.open_connection(HOST, PORT, ssl=True)

    # Identify ourselves
    writer.write(f'USER {NICK} 0 * :{NICK}\r\n'.encode())
    writer.write(f'NICK {NICK}\r\n'.encode())
    await writer.drain()

    # Join #bots channel
    writer.write(f'JOIN #bots\r\n'.encode())
    await writer.drain()

    # Write message to channel
    writer.write(f"PRIVMSG #bots :I've fallen and I can't get up!\r\n".encode())
    await writer.drain()

    # Read and display
    while True:
        message = (await reader.readline()).decode().strip()
        print(message)

# Main execution

def main():
    asyncio.run(ircle())

if __name__ == '__main__':
    main()

Verification

To get credit for this Guru Point, you must have your IRC bot join the #bots channel on the chat.ndlug.org server and demonstrate its functionality yourself (by logging into the server with your own account) and showing the instructor. You have up until Friday, May 5 to verify your guru point.

VPS (1 Point)

For extra extra credit, you are to sign up for virtual private server on a service such as Digital Ocean, Linode, Amazon Web Services, Microsoft Azure, Google Cloud, or Vultr and run the loki.py HTTP server from that VPS.

GitHub Student Developer Pack

As mentioned in class, you are eligible for the GitHub Student Developer Pack, which provides you with credits to Digital Ocean and Microsoft Azure (among many other free goodies).

Verification

To get credit for this Guru Point, you must show either a TA a demonstration of using thor to hammer loki.py running on your VPS (or attach a video / screenshot to your Pull Request). You have up until Friday, May 5 to verify your guru point.

Submission

To submit your assignment, please commit your work to the homework09 folder in your assignments GitHub repository. Your homework09 folder should only contain at the following files:

Note: You must not modify any of the provided test scripts or unit tests.

#--------------------------------------------------
# BE SURE TO DO THE PREPARATION STEPS IN ACTIVITY 0
#--------------------------------------------------

$ cd homework09                           # Go to Homework 09 directory
...
$ git add Makefile                        # Mark changes for commit
$ git add bin/*.sh bin/*.py               # Mark changes for commit
$ git add include/*.h                     # Mark changes for commit
$ git add lib/.gitkeep                    # Mark changes for commit
$ git add tests/*.c                       # Mark changes for commit
$ git commit -m "homework09: import"      # Record changes
...
$ git add src/timestamp.c                 # Mark changes for commit
$ git commit -m "homework09: timestamp"   # Record changes
...
$ git add src/socket.c                    # Mark changes for commit
$ git commit -m "homework09: socket"      # Record changes
...
$ git add src/request.c                   # Mark changes for commit
$ git commit -m "homework09: request"     # Record changes
...
$ git add src/hammer.c                    # Mark changes for commit
$ git commit -m "homework09: hammer"      # Record changes
...
$ git add src/thor.c                      # Mark changes for commit
$ git commit -m "homework09: thor"        # Record changes
...
$ git add answers.json                    # Mark changes for commit
$ git commit -m "homework09: quiz"        # Record changes
$ git push -u origin homework09           # Push branch to GitHub

Pull Request

Remember to create a Pull Request and assign the appropriate TA from the Reading 13 TA List.

DO NOT MERGE your own Pull Request. The TAs use open Pull Requests to keep track of which assignments to grade. Closing them yourself will cause a delay in grading and confuse the TAs.