Initial commit, just gotta fix README
This commit is contained in:
commit
89a5fe9824
|
@ -0,0 +1,2 @@
|
|||
*.pyc
|
||||
__pycache__/*
|
|
@ -0,0 +1,3 @@
|
|||
.PHONY: test
|
||||
test:
|
||||
sudo ncat -l 80 -k --sh-exec "./serve"
|
|
@ -0,0 +1,3 @@
|
|||
# PyServ - Python server (use with `ncat` or similar)
|
||||
|
||||
A Python framework for HTTP servers. Requires ncat or similar.
|
|
@ -0,0 +1,5 @@
|
|||
# MineRobber's blog generator
|
||||
Made with Python, love, care, and [markdown2](https://github.com/trentm/python-markdown2)!
|
||||
|
||||
##Configuration
|
||||
It's all configured already.
|
|
@ -0,0 +1,66 @@
|
|||
import cPickle, subprocess, tempfile, markdown2, config, time, argparse, tempfile, os;
|
||||
#print("MineRobber's blog generator")
|
||||
#print("------------------------")
|
||||
try:
|
||||
blog_posts = cPickle.load(open("posts.pickled","rb"))
|
||||
except:
|
||||
blog_posts = []
|
||||
parser = argparse.ArgumentParser(description="A blog generator.")
|
||||
parser.add_argument("--workflow",help="Run the basic workflow (write a new post, generate the page, and quit)",action="store_true")
|
||||
parser.add_argument("--generate",help="Generate the page and quit",action="store_true")
|
||||
args = parser.parse_args()
|
||||
if not args.workflow and not args.generate:
|
||||
print("MineRobber's blog generator")
|
||||
print("------------------------")
|
||||
print("'n' for new post, 'g' to generate, 'q' to quit.")
|
||||
running = True;
|
||||
def tweetlink():
|
||||
return config.tweetlink_do and config.tweetlink_location and config.tweetlink_url
|
||||
def loop(choice):
|
||||
if choice.lower() == "n":
|
||||
title = raw_input("Title: ")
|
||||
bodyf = tempfile.NamedTemporaryFile()
|
||||
os.system("nano {}".format(bodyf.name))
|
||||
body = markdown2.markdown(bodyf.read().rstrip())
|
||||
bodyf.close()
|
||||
blog_posts.append([title, body])
|
||||
elif choice.lower() == "g":
|
||||
rss = "<?xml version='1.0' encoding='UTF-8'?><rss version='2.0'><channel><title>"+config.title+"</title><link>"+config.url+"</link><description>"+config.desc+"</description>"
|
||||
contents = "<html><head><title>{0}</title><link rel='stylesheet' href='{1}'></head><body><h1>{0}</h1>".format(config.title,config.css_location)
|
||||
blog_posts.reverse()
|
||||
i = len(blog_posts)
|
||||
for post in blog_posts:
|
||||
contents += "<h2 id='post"+str(i)+"'>"+post[0]+"</h2>"+post[1]
|
||||
if tweetlink():
|
||||
contents += "<p><a href='{}'>Tweet this</a></p>".format(config.tweetlink_url+"#post"+str(i))
|
||||
rss += "<item><title>{0}</title><link>{3}#post{1!s}</link><description>{2}</description></item>".format(post[0],i,post[1][3:-5].replace("<","<").replace(">",">"),config.url)
|
||||
i -= 1
|
||||
blog_posts.reverse()
|
||||
# remove footer
|
||||
# contents += "<hr><p>Generated by <a href='https://github.com/MineRobber9000/blog-gen'>MineRobber's Python blog generator.</a><br><a href='{}'>RSS Feed</a></p>".format(config.rss_url)
|
||||
contents += "</body></html>"
|
||||
rss += "</channel></rss>"
|
||||
with open(config.location,"wb") as f:
|
||||
f.write(contents);
|
||||
with open(config.rss_location,"wb") as f2:
|
||||
f2.write(rss)
|
||||
if tweetlink():
|
||||
with open(config.tweetlink_location,"wb") as f3:
|
||||
f3.write("<script>document.location='https://twitter.com/home?status={}'+document.location.hash.split('').slice(1).join('')</script>".format(config.tweetlink_format).format(config.title,config.url))
|
||||
elif choice.lower() == "q":
|
||||
return False
|
||||
else:
|
||||
print("Unknown option: "+choice)
|
||||
cPickle.dump(blog_posts,open("posts.pickled","wb"))
|
||||
return True
|
||||
if not args.workflow and not args.generate:
|
||||
while running:
|
||||
running = loop(raw_input("> "))
|
||||
print("bye")
|
||||
elif not args.workflow and args.generate:
|
||||
loop("g")
|
||||
loop("q")
|
||||
else:
|
||||
loop("n")
|
||||
loop("g")
|
||||
loop("q")
|
|
@ -0,0 +1,12 @@
|
|||
title = "Minecraft News"
|
||||
url = "http://mcupdate.tumblr.com"
|
||||
location = "/home/minerobber/mcuserv/mcus/index.html"
|
||||
rss_location = "/home/minerobber/mcuserv/mcus/feed.xml"
|
||||
rss_url = "http://mcupdate.tumblr.com/feed.xml"
|
||||
desc = "Minecraft News replacement with static files"
|
||||
css_location = "http://mcupdate.tumblr.com/index.css"
|
||||
tweetlink_do = False
|
||||
tweetlink_location = ""
|
||||
tweetlink_url = ""
|
||||
tweetlink_format = "I just read an article from \"{}\":"
|
||||
tweetlink_format += " {}%23" # don't change this!
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
|||
(lp1
|
||||
(lp2
|
||||
S'Test'
|
||||
p3
|
||||
accopy_reg
|
||||
_reconstructor
|
||||
p4
|
||||
(cmarkdown2
|
||||
UnicodeWithAttrs
|
||||
p5
|
||||
c__builtin__
|
||||
unicode
|
||||
p6
|
||||
V<p>This is a test, stupid.</p>\u000a
|
||||
tRp7
|
||||
aa.
|
|
@ -0,0 +1,68 @@
|
|||
import pyserv, sys
|
||||
|
||||
# Adding response headers to this list will put them in any response that doesn't need specific headers.
|
||||
DEFAULT_RESPONSE_HEADERS = dict(Server=pyserv.VERSION)
|
||||
|
||||
ending_mimes = {}
|
||||
ending_mimes["html"] = "text/html"
|
||||
ending_mimes["css"] = "text/css"
|
||||
ending_mimes["png"] = "image/png"
|
||||
|
||||
class TestServ(pyserv.HTTPServer):
|
||||
def __init__(self):
|
||||
# Initialize base class.
|
||||
super().__init__()
|
||||
# Register GET and POST methods.
|
||||
self.register("GET",self.do_GET)
|
||||
self.register("POST",self.do_POST)
|
||||
# Add registries for other reasons
|
||||
self.contents = dict()
|
||||
self.mime = dict()
|
||||
self.cgi = dict()
|
||||
self.post = dict()
|
||||
# Add index.html content to be statically served.
|
||||
# with open("mcus.html") as f:
|
||||
# self.registerPage("/",[l.rstrip() for l in f],"text/html")
|
||||
# self.registerCGI("/",lambda x,y: [m.replace("PATH",x) for m in self.contents["/"]])
|
||||
|
||||
def registerPage(self,path,content,mime="text/plain"):
|
||||
self.contents[path] = content
|
||||
self.mime[path] = mime
|
||||
|
||||
def registerCGI(self,path,func):
|
||||
self.cgi[path]=func
|
||||
|
||||
def registerPost(self,path,func):
|
||||
self.post[path] = func
|
||||
|
||||
# def greetingform(self,path,headers,data):
|
||||
# with open("greetings.html") as f:
|
||||
# return 200, DEFAULT_RESPONSE_HEADERS, [l.rstrip().format(pyserv.lazySan(data['name'][0] if data.get('name') else "stranger")) for l in f]
|
||||
|
||||
def do_GET(self,method,path,headers):
|
||||
resp_headers = dict(Server=pyserv.VERSION)
|
||||
# with open("mcus.html") as f:
|
||||
# contents = [l.rstrip() for l in f]
|
||||
print("GET {}".format(path),file=sys.stderr)
|
||||
path = path if path!="/" else "index.html"
|
||||
try:
|
||||
with open("mcus/{}".format(path)) as f:
|
||||
contents = [f.read()]
|
||||
if path.split(".")[-1] in ending_mimes:
|
||||
resp_headers["Content-Type"] = ending_mimes[path.split(".")[-1]]
|
||||
except FileNotFoundError:
|
||||
return pyserv.abort(404)
|
||||
return self.formatResponse(200,resp_headers,contents) # Send contents along.
|
||||
|
||||
def do_POST(self,m,path,headers,data):
|
||||
if path in self.post:
|
||||
try:
|
||||
# CGI functions return response code, response headers, and contents.
|
||||
respcode, resp_headers, contents = self.post[path](path,headers,pyserv.unquote(data))
|
||||
# This data is directly passed into formatResponse to get a formatted response.
|
||||
return self.formatResponse(respcode,resp_headers,contents)
|
||||
except pyserv.Abort404: # Abort404 error is a signal
|
||||
return pyserv.abort(404)
|
||||
except: # If something else goes wrong, it's an internal error.
|
||||
return pyserv.abort(500)
|
||||
return pyserv.abort(405) # If path isn't in self.post, we aren't going to bother asking GET for it.
|
Binary file not shown.
After Width: | Height: | Size: 434 B |
|
@ -0,0 +1 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?><rss version='2.0'><channel><title>Minecraft News</title><link>http://mcupdate.tumblr.com</link><description>Minecraft News replacement with static files</description><item><title>Test</title><link>http://mcupdate.tumblr.com#post1</link><description>This is a test, stupid.</description></item></channel></rss>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,35 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
background-color: #222222;
|
||||
background-image: url('https://static.tumblr.com/qfn3sex/ezom7y7iq/bg_main.png');
|
||||
color: #e0d0d0;
|
||||
|
||||
}
|
||||
a {
|
||||
color: #aaaaff;
|
||||
}
|
||||
hr {
|
||||
border: 0;
|
||||
color: #111111;
|
||||
background-color: #111111;
|
||||
height: 2px;
|
||||
}
|
||||
h3 {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
}
|
||||
img {
|
||||
border:0;
|
||||
margin:0;
|
||||
}
|
||||
.sidebar {
|
||||
padding:10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.alert {
|
||||
background-color:#aa0000;
|
||||
color:#ffffff;
|
||||
font-weight:bold;
|
||||
padding: 6px 10px;
|
||||
width:500px;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<html><head><title>Minecraft News</title><link rel='stylesheet' href='http://mcupdate.tumblr.com/index.css'></head><body><h1>Minecraft News</h1><h2 id='post1'>Test</h2><p>This is a test, stupid.</p>
|
||||
</body></html>
|
|
@ -0,0 +1,66 @@
|
|||
from urllib.parse import parse_qs as upunquote
|
||||
VERSION = "PyServ Beta 0.1"
|
||||
|
||||
class Abort404(Exception):
|
||||
"""Signals for a 404 error, if listened for"""
|
||||
def __init__(self,*args):
|
||||
super().__init__(*args)
|
||||
|
||||
# Unquotes query strings.
|
||||
def unquote(*args):
|
||||
return upunquote(*args)
|
||||
|
||||
# Lazy text sanitizer.
|
||||
def lazySan(s):
|
||||
return s.replace("<","<").replace("&","&")
|
||||
|
||||
class HTTPServer:
|
||||
"""HTTP Server base class"""
|
||||
def __init__(self):
|
||||
self.methods = dict()
|
||||
|
||||
"""Registers function to handle requests to a method"""
|
||||
def register(self,method,f):
|
||||
self.methods[method]=f
|
||||
|
||||
"""Returns whether a function is set to handle the given method"""
|
||||
def hasMethod(self,method):
|
||||
return method in self.methods.keys()
|
||||
|
||||
"""Returns a list of supported methods."""
|
||||
def getMethods(self):
|
||||
ret = list(self.methods.keys())
|
||||
ret.sort()
|
||||
return ret
|
||||
|
||||
"""Formats a response."""
|
||||
def formatResponse(self,respcode,headers,content):
|
||||
lines = []
|
||||
lines.append("HTTP/1.1 {!s} {}".format(respcode,DEFAULT_MSGS[respcode]))
|
||||
for h in headers:
|
||||
lines.append("{}: {}".format(h,headers[h]))
|
||||
lines.append("") # of course
|
||||
for line in content:
|
||||
lines.append(line)
|
||||
return "\n".join(lines)+"\n"
|
||||
|
||||
# This function creates a stock error page.
|
||||
# TODO: Replace DEFAULT_MSGS with maintained database
|
||||
DEFAULT_MSGS = {100: "Continue",101: "Switching Protocols",102: "Processing",200: "Ok",201: "Created",202: "Accepted",203: "Non Authoritative Information",204: "No Content",205: "Reset Content",206: "Partial Content",207: "Multi Status",208: "Already Reported",226: "Im Used",300: "Multiple Choices",301: "Moved Permanently",302: "Found",303: "See Other",304: "Not Modified",305: "Use Proxy",307: "Temporary Redirect",308: "Permanent Redirect",400: "Bad Request",401: "Unauthorized",402: "Payment Required",403: "Forbidden",404: "Not Found",405: "Method Not Allowed",406: "Not Acceptable",407: "Proxy Authentication Required",408: "Request Timeout",409: "Conflict",410: "Gone",411: "Length Required",412: "Precondition Failed",413: "Request Entity Too Large",414: "Request Uri Too Long",415: "Unsupported Media Type",416: "Requested Range Not Satisfiable",417: "Expectation Failed",418: "I'm A Teapot",422: "Unprocessable Entity",423: "Locked",424: "Failed Dependency",426: "Upgrade Required",428: "Precondition Required",429: "Too Many Requests",431: "Request Header Fields Too Large",500: "Internal Server Error",501: "Not Implemented",502: "Bad Gateway",503: "Service Unavailable",504: "Gateway Timeout",505: "Http Version Not Supported",506: "Variant Also Negotiates",507: "Insufficient Storage",508: "Loop Detected",510: "Not Extended",511: "Network Authentication Required"}
|
||||
def abort(errorcode,message=None,headers=dict()):
|
||||
lines = []
|
||||
if message is None:
|
||||
message = DEFAULT_MSGS.get(errorcode,"Unknown response code")
|
||||
lines.append("HTTP/1.1 {!s} {}".format(errorcode,message))
|
||||
headers["Server"] = headers.get("Server",VERSION)
|
||||
if errorcode!=204 and "Content-Type" not in headers:
|
||||
headers["Content-Type"] = "text/html"
|
||||
for h in headers:
|
||||
lines.append("{}: {}".format(h,headers[h]))
|
||||
if errorcode==204:
|
||||
return "\n".join(lines)+"\n" # No content. Duh.
|
||||
lines.append("")
|
||||
lines.append("<h1>{!s} {}</h1>".format(errorcode, message))
|
||||
lines.append("<hr>")
|
||||
lines.append("<p>{}</p>".format(headers["Server"]))
|
||||
return "\n".join(lines)+"\n"
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/python
|
||||
import sys, pyserv, mcus
|
||||
|
||||
# Change this to change which server class gets used. Should be a subclass of pyserv.HTTPServer.
|
||||
server = mcus.TestServ()
|
||||
|
||||
# Example registration (in server __init__ method)
|
||||
# self.register("GET",lambda m,p,h: pyserv.abort(418))
|
||||
# The above will make any GET request return the I'm a Teapot code.
|
||||
|
||||
# Distance from end of string to literal "HTTP". Should be 8 in all requests.
|
||||
def httpDistance(l):
|
||||
return (l[::-1].find("PTTH"))+len("PTTH")
|
||||
|
||||
# Read all lines of data sent to us by browser/client
|
||||
inlines = []
|
||||
a = sys.stdin.readline().rstrip()
|
||||
while a:
|
||||
inlines.append(a)
|
||||
a = sys.stdin.readline().rstrip()
|
||||
# Validate HTTP request
|
||||
# In all valid HTTP requests, "HTTP/X.X" is 8 characters from the end.
|
||||
if httpDistance(inlines[0])!=8:
|
||||
print(pyserv.abort(400))
|
||||
# Parse headers
|
||||
headers = dict()
|
||||
for l in inlines[1:]:
|
||||
p = l.split(": ",1)
|
||||
if p[0] in headers:
|
||||
continue
|
||||
headers[p[0]] = p[1]
|
||||
# Parse request
|
||||
method, path, version = inlines[0].split(" ")
|
||||
# If the server doesn't have the method, it isn't implemented.
|
||||
if not server.hasMethod(method):
|
||||
print(pyserv.abort(501))
|
||||
if method=="POST":
|
||||
# Read query-string and call function
|
||||
print(server.methods[method](method, path, headers, sys.stdin.read(int(headers["Content-Length"]))))
|
||||
else:
|
||||
# Just call the function
|
||||
print(server.methods[method](method, path, headers))
|
Loading…
Reference in New Issue