~callum/http-string-store

ee9fb3280786330e3fa57351aa4b74d99ae21950 — Callum Brown 3 years ago
Initial commit
3 files changed, 153 insertions(+), 0 deletions(-)

A .gitignore
A README.md
A server.py
A  => .gitignore +2 -0
@@ 1,2 @@
__pycache__/
*.db

A  => README.md +53 -0
@@ 1,53 @@
# HTTP String Store

This is a Python Flask application which provides a simple HTTP API for storing
and retrieving UTF-8 strings.


## API Documentation

GET /new
Returns the ID of a new string store in the response body.
The Content-Type header of the response will be "text/plain; charset=UTF-8".

POST /s/<id>
Set the string with the given ID to the string in the request body.
If the given ID is not already in use the server will return a "404 Not Found error".
The Content-Type header of the request must be "text/plain; charset=UTF-8";
if it is not the server will return a "415 Unsupported Media Type" error.
The Server may reject strings it deems too long by returning a "413 Payload Too Large" error.

GET /s/<id>
Returns the string with the given ID in the response body.
The Content-Type header of the response will be "text/plain; charset=UTF-8".


## Server Setup

Requirements:

- Python 3
- flask

Clone the repository and change to it's directory.

```
$ python -c "import server; server.setup_database()"
$ export FLASK_APP=server.py
$ flask run
```

Or you can use a different WSGI server.
You may edit the constants at the start of server.py to your liking.


## Example using Curl

```
$ curl 'http://example.org/new'
Ig7WRA4w
$ 'http://example.org/s/Ig7WRA4w' -H "Content-Type: text/plain; charset=UTF-8" -d "Hello, World!"
success
$ curl 'http://example.org/s/Ig7WRA4w'
Hello, World!
```

A  => server.py +98 -0
@@ 1,98 @@
import os
import sqlite3
import random
import string
from flask import Flask, g, request, abort, make_response


ID_LENGTH = 8
ID_CHARACTERS = string.ascii_letters + string.digits
STRING_MAX_BYTES = 1024
DB_PATH = os.path.dirname(os.path.abspath(__file__)) + "/string_store.db"


def setup_database():
    db = sqlite3.connect(DB_PATH)
    db.execute('''
        CREATE TABLE IF NOT EXISTS strings (
            id TEXT PRIMARY KEY,
            string STRING
        )
    ''')
    db.commit()
        

def get_db():
    if 'db' not in g:
        # only create db object if not already done in this request.
        g.db = sqlite3.connect(DB_PATH)
        g.db.row_factory = sqlite3.Row
    return g.db


def close_db(e=None):
    db = g.pop('db', None)
    if db is not None:
        db.close()


def string_from_id(sid):
    db = get_db()
    r = db.execute("SELECT string FROM strings WHERE id=?", (sid,)).fetchone()
    if r:
        return r["string"]
    else:
        return None


def response(s):
    r = make_response(s)
    r.headers["Content-Type"] = "text/plain; charset=UTF-8"
    return r


app = Flask(__name__)
app.teardown_appcontext(close_db)


@app.route("/new", methods=["GET"])
def new():
    db = get_db()
    unique = False
    while not unique:
        new_id = "".join(
            random.choice(ID_CHARACTERS) for i in range(ID_LENGTH)
        )
        try:
            db.execute("INSERT INTO strings VALUES (?,?)", (new_id, ""))
            unique = True
        except sqlite3.IntegrityError:
            # There is already a string with that ID
            unique = False
    db.commit()
    return response(new_id)


@app.route("/s/<string:sid>", methods=["GET"])
def get_string(sid):
    s = string_from_id(sid)
    if s is None:
        abort(404)
    return response(s)


@app.route("/s/<string:sid>", methods=["POST"])
def set_string(sid):
    existing_s = string_from_id(sid)
    if existing_s is None:
        abort(404)
    print(request.content_type)
    if request.content_type != "text/plain; charset=UTF-8":
        abort(415)
    if request.content_length > STRING_MAX_BYTES:
        abort(413)
    s = request.get_data(as_text=True)
    db = get_db()
    db.execute("UPDATE strings SET string=? WHERE id=?", (s, sid))
    db.commit()
    return response("success")