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.
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.
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.
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.
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.
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:
host
contains the host component of a URL.port
contains the port component of a URL.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.
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:
example.com
http://example.com
http://example.com:8888
http://example.com/data
example.com:9999/data
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.
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:
You must use the CC
, CFLAGS
, LD
, LDFLAGS
, AR
, and ARFLAGS
variables when appropriate in your rules.
Note: Although we are producing a static library in the form of
libthor.a
, we will not be statically linking our executables.
Instead, we will use the libthor.a
as another object file when we
link our executables.
Note: The binary executables must go in the bin
folder.
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.
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:
double timestamp()
This function returns a
double
that corresponds to the current time in seconds.
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
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:
FILE *socket_dial(const char *host, const char *port)
This function uses socket to connect to the given
host
andport
using TCP and returns a read/writableFILE
stream corresponding to the socket.
You must check if any of the system calls fail.
You may wish to use getaddrinfo, socket, connect, freeaddrinfo, and fdopen.
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
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:
Request * request_create(const char *url)
This function allocates a new
Request
struct, sets itshost
,port
, andpath
fields based on the components in the givenurl
, and returns the allocated struct.
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.
void request_delete(Request *request)
This function deallocates the given
Request
struct and any previously allocated fields such ashost
,port
, andpath
.
ssize_t request_stream(const Request *request, FILE *stream)
This function makes a HTTP request with the given
Request
struct by usingsocket_dial
and writes the response body to the givenstream
. It returns the number of bytes written to the outputstream
or-1
if any error was experienced during the HTTP request.
You must check if any of the system calls fail.
You will want to follow the HTTP transaction steps described above and outlined below:
stream
.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
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:
bool hammer(const URL *url, FILE *stream)
This function performs a HTTP request by creating a
Request
struct andrequest_stream
. It will measure the bandwidth by using thetimestamp
function to compute the total elapsed time of the HTTP request and then dividing it by the number of bytes written to thestream
. It will returntrue
if the HTTP request was successful.
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
.
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 thehammer
function on the givenurl
andstream
. 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 returntrue
if all child processes reported success, otherwisefalse
.
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
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
.
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
.
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:
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
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
For this week, there are three Guru Point opportunities.
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.
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.
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.
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:
To connect to the NDLUG server yourself, you can use the following anonymous web client:
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!
The USER
command sets the users real name and registers the user.
The NICK
command sets the users nickname.
The JOIN
command allows the user to join in a channel (in this case #bots
).
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()
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.
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.
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).
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.
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:
Makefile
README.md
answers.json
bin/loki.py
bin/test_hammer.sh
bin/test_socket.sh
bin/test_request.sh
bin/test_thor.sh
bin/test_timestamp.sh
include/hammer.h
include/macros.h
include/request.h
include/socket.h
include/timestamp.h
lib/.gitkeep
src/hammer.c
src/request.c
src/socket.c
src/thor.c
src/timestamp.c
tests/unit_hammer.c
tests/unit_request.c
tests/unit_socket.c
tests/unit_timestamp.c
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
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.