Introduction
PyWeb provides a webserver solution for all operating systems without a boat-load of instructions or management. This article is about setting it up and how to implement your own plugins.
Setup Instructions
PyWeb requires Python 2.7. Once Python 2.7 is installed, create a folder wherever you would like to run the server, then clone the github repo, or extract the provided project zip, then launch __init__.py. It's that simple!
Using the Code
Using the code involves making plugins. You should not edit the source without advanced knowledge in what it is doing and how webservers communicate. I used PyCharm Community IDE to program everything, and I would suggest you use the same.
I use certain terminology to describe certain things. These include, but are not limited to:
- Delegate/Delegate function - The function to be called included in the plug in, registered using "
@delegate.delegate_type
" - Delegated/Delegated function - The original function to be modified. Added using "
Plugin.create_delegate
" - "Original" - Delegated function
- Return object of... - Although this should be obvious, this is the object that is returned when the "..." is called.
PyWeb works by using plugins to manipulate return objects. In order to override a function, you have to use a delegate. Delegates come in three forms:
Callbefore
- Calls the delegate function BEFORE the delegated function WITH the same arguments used to call the delegated function. Callback
- Calls the delegate function AFTER the delegated function WITH the delegated function's return object. Callwith
- Calls the delegate function AFTER the delegated function WITH the same arguments used to call the delegated function.
Callback calls the function AFTER the original has been called. This call's parameters include the return object of the delegated function. For example:
import Plugin
response = Plugin.create_delegate("Responses", "Response")
@response.callback
def response_callback(responseobj):
responseobj.method = "OPTIONS"
return responseobj
The Response
callback in that case will always give "OPTIONS
" as the method used, regardless of any other parameters. This is important to note because when someone makes a request, the response will always be OPTIONS
, even if the request was for a file using the method "GET
." One must always use "import plugin
" to add a delegate. All plugins located in "./plugins" will be executed on server startup, but they won't be persistent unless you make it. This could be accomplished by creating a while
loop, which would block all threads, or you could just register a delegate, like I did above, and whenever a Response
object is created, the "response_callback
" function will be called.
The next example is a callbefore
function. This can be used to change the arguments as the delegated function will use whatever is returned from the callbefore
. Example:
import Plugin
openfunc = Plugin.create_delegate("FileManager", "open")
@openfunc.callbefore
def change_path(path):
path = "C:\\Users\\Brandon\\Documents\\I_want_this_file_instead_always.jpg"
return path
The example above will always provide the new path for every time "open
" is called in the FileManager
. It is important to note that this will happen on all occasions. So, now, when someone makes a GET
request, the file opened will be "C:\\Users\\Brandon\\Documents\\I_want_this_file_instead_always.jpg"
Caution: Doing this will make it so EVERY get request made will give the same path. It's not particularly useful, but you can see why it's necessary.
Lastly, we have callwith
. Callwith
is essentially the same as a callback, but instead of the object returned as a parameter, it will use the arguments that were originally passed to the original object. Whatever callwith
returns will replace the object as a whole. For example:
import Plugin
from HTTP import Response
replaced_response = Plugin.create_delegate("Responses", "Response")
@replaced_response.callwith
def replace_response(requestobj):
requestobj.connection("terminate")
return Response(requestobj)
In this case, we have a Response
object returned, just like it normally would be, but instead, we are using "requestobj
" with a modified connection header. Since we are changing the connection header of the request to "Terminate
", the Response
will send the "Connection: Terminated
" header, to terminate the connection. This could potentially be useful for getting information from the Request
and changing it to return a better-tailored Response
. Another great way of using callwith
is for tracing output. Say you are getting a "rogue" message from a client connection. You could output what it is sending so you could then create a way of handling it.
Caution: When using "callwith
", all objects that use the overwritten object still call the same methods. For example: "Response.generate()
" is called when generating a Response
to send back to the host. If you replace the Response
object with a string
, it will result in a fatal error as string
s do not have the function "generate
."
Note: None of these delegates MUST return anything. If a delegate doesn't return a new object, the default will be used. If any of these functions do return an object, however, the object that is returned will be used respectively.
Summary
As you can see, the possibilities are virtually endless. If you don't complicate things by editing the source and just use the plugin framework, you shouldn't have any problems. The Webserver isn't 100% completed, there are a lot of methods yet to be implemented, but basic functionality still works.
Points of Interest
The Hyper-Text Transfer Protocol is an advanced and standardized system. Communication between server and client is very strict and any messages that are transferred without proper knowledge will either result in error or funky things.
Want to help develop PyWeb?
Contact me either on forums, github, or by email at beemerwt@gmail.com
I'm looking for programmers to help develop PyWeb.
History
- 11/16/2016
- Updated
<pre>
tags for Python - Repository was updated, changes to code accordingly
- Added setup instructions
- 11/17/2016
- Updated <pre> tags, again, changed "plug in" to "Plugin" - for some reason it was changed
- If you want to develop, contact me