Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/DuetNG/DuetEthernet/Webserver.h')
-rw-r--r--src/DuetNG/DuetEthernet/Webserver.h386
1 files changed, 386 insertions, 0 deletions
diff --git a/src/DuetNG/DuetEthernet/Webserver.h b/src/DuetNG/DuetEthernet/Webserver.h
new file mode 100644
index 00000000..3e4f2c30
--- /dev/null
+++ b/src/DuetNG/DuetEthernet/Webserver.h
@@ -0,0 +1,386 @@
+/****************************************************************************************************
+
+RepRapFirmware - Webserver
+
+This class serves a single-page web applications to the attached network. This page forms the user's
+interface with the RepRap machine. This software interprests returned values from the page and uses it
+to Generate G Codes, which it sends to the RepRap. It also collects values from the RepRap like
+temperature and uses those to construct the web page.
+
+The page itself - reprap.htm - uses Knockout.js and Jquery.js. See:
+
+http://knockoutjs.com/
+
+http://jquery.com/
+
+-----------------------------------------------------------------------------------------------------
+
+Version 0.2
+
+10 May 2013
+
+Adrian Bowyer
+RepRap Professional Ltd
+http://reprappro.com
+
+Licence: GPL
+
+****************************************************************************************************/
+
+#ifndef WEBSERVER_H
+#define WEBSERVER_H
+
+#include "NetworkDefs.h"
+#include "RepRapFirmware.h"
+#include "MessageType.h"
+#include "Storage/FileData.h"
+
+/* Generic values */
+
+const size_t gcodeBufferLength = 512; // size of our gcode ring buffer, preferably a power of 2
+
+/* HTTP */
+
+#define KO_START "rr_"
+#define KO_FIRST 3
+
+const uint16_t webMessageLength = TCP_MSS; // maximum length of the web message we accept after decoding
+const size_t minHttpResponseSize = 768; // minimum number of bytes required for an HTTP response
+
+const size_t maxCommandWords = 4; // max number of space-separated words in the command
+const size_t maxQualKeys = 5; // max number of key/value pairs in the qualifier
+const size_t maxHeaders = 16; // max number of key/value pairs in the headers
+
+const size_t maxHttpSessions = 8; // maximum number of simultaneous HTTP sessions
+const uint32_t httpSessionTimeout = 8000; // HTTP session timeout in milliseconds
+
+/* FTP */
+
+const uint16_t ftpMessageLength = 128; // maximum line length for incoming FTP commands
+const uint32_t ftpPasvPortTimeout = 10000; // maximum time to wait for an FTP data connection in milliseconds
+
+/* Telnet */
+
+const uint32_t telnetSetupDuration = 4000; // ignore the first Telnet request within this duration (in ms)
+
+
+class Webserver;
+
+// List of protocols that can execute G-Codes
+enum class WebSource
+{
+ HTTP,
+ Telnet
+};
+
+// This is the abstract class for all supported protocols
+// Any inherited class should implement a state machine to increase performance and reduce memory usage.
+class ProtocolInterpreter
+{
+ public:
+
+ ProtocolInterpreter(Platform *p, Webserver *ws, Network *n);
+ virtual ~ProtocolInterpreter() { } // to keep Eclipse happy
+ virtual void Diagnostics(MessageType mtype) = 0;
+ virtual void Spin();
+
+ virtual void ConnectionEstablished();
+ virtual void ConnectionLost(Connection conn /*const ConnectionState *cs*/) { }
+ virtual bool CanParseData();
+ virtual bool CharFromClient(const char c) = 0;
+ virtual void NoMoreDataAvailable();
+
+ virtual bool DoingFastUpload() const;
+ virtual void DoFastUpload();
+ void CancelUpload(); // may be called from ISR!
+
+ protected:
+
+ Platform *platform;
+ Webserver *webserver;
+ Network *network;
+
+ // Information for file uploading
+ enum UploadState
+ {
+ notUploading, // no upload in progress
+ uploadOK, // upload in progress, no error so far
+ uploadError // upload in progress but had error
+ };
+
+ UploadState uploadState;
+ FileData fileBeingUploaded;
+ char filenameBeingUploaded[FILENAME_LENGTH];
+
+ bool StartUpload(FileStore *file, const char *fileName);
+ bool IsUploading() const;
+ bool FinishUpload(uint32_t fileLength);
+};
+
+class Webserver
+{
+public:
+
+ friend class Platform;
+ friend class ProtocolInterpreter;
+
+ Webserver(Platform* p, Network *n);
+ void Init();
+ void Spin();
+ void Exit();
+ void Diagnostics(MessageType mtype);
+
+ bool GCodeAvailable(const WebSource source) const;
+ char ReadGCode(const WebSource source);
+ void HandleGCodeReply(const WebSource source, OutputBuffer *reply);
+ void HandleGCodeReply(const WebSource source, const char *reply);
+ uint32_t GetReplySeq() const;
+
+ // Returns the available G-Code buffer space of the HTTP interpreter (may be dropped in a future version)
+ uint16_t GetGCodeBufferSpace(const WebSource source) const;
+
+ void ConnectionLost(Connection conn /*const ConnectionState *cs*/);
+ void ConnectionError();
+
+protected:
+
+ class HttpInterpreter : public ProtocolInterpreter
+ {
+ public:
+
+ HttpInterpreter(Platform *p, Webserver *ws, Network *n);
+ void Spin();
+ void Diagnostics(MessageType mtype) override;
+ void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
+ bool CanParseData() override;
+ bool CharFromClient(const char c) override;
+ void NoMoreDataAvailable() override;
+ void ResetState();
+ void ResetSessions();
+
+ bool DoingFastUpload() const override;
+ void DoFastUpload();
+
+ bool GCodeAvailable() const;
+ char ReadGCode();
+ void HandleGCodeReply(OutputBuffer *reply);
+ void HandleGCodeReply(const char *reply);
+ uint16_t GetGCodeBufferSpace() const;
+ uint32_t GetReplySeq() const;
+
+ private:
+
+ // HTTP server state enumeration. The order is important, in particular xxxEsc1 must follow xxx, and xxxEsc2 must follow xxxEsc1.
+ // We assume that qualifier keys do not contain escapes, because none of ours needs to be encoded. If we are sent escapes in the key,
+ // it won't do any harm, but the key won't be recognised even if it would be valid were it decoded.
+ enum HttpState
+ {
+ doingCommandWord, // receiving a word in the first line of the HTTP request
+ doingFilename, // receiving the filename (second word in the command line)
+ doingFilenameEsc1, // received '%' in the filename (e.g. we are being asked for a filename with spaces in it)
+ doingFilenameEsc2, // received '%' and one hex digit in the filename
+ doingQualifierKey, // receiving a key name in the HTTP request
+ doingQualifierValue, // receiving a key value in the HTTP request
+ doingQualifierValueEsc1, // received '%' in the qualifier
+ doingQualifierValueEsc2, // received '%' and one hex digit in the qualifier
+ doingHeaderKey, // receiving a header key
+ expectingHeaderValue, // expecting a header value
+ doingHeaderValue, // receiving a header value
+ doingHeaderContinuation // received a newline after a header value
+ };
+ HttpState state;
+
+ struct KeyValueIndices
+ {
+ const char* key;
+ const char* value;
+ };
+
+ void SendFile(const char* nameOfFileToSend, bool isWebFile);
+ void SendGCodeReply();
+ void SendJsonResponse(const char* command);
+ void GetJsonResponse(const char* request, OutputBuffer *&response, bool& keepOpen);
+ bool ProcessMessage();
+ bool RejectMessage(const char* s, unsigned int code = 500);
+
+ // Buffers for processing HTTP input
+ char clientMessage[webMessageLength + 3]; // holds the command, qualifier, and headers
+ size_t clientPointer; // current index into clientMessage
+ char decodeChar;
+
+ const char* commandWords[maxCommandWords];
+ KeyValueIndices qualifiers[maxQualKeys + 1]; // offsets into clientQualifier of the key/value pairs, the +1 is needed so that values can contain nulls
+ KeyValueIndices headers[maxHeaders]; // offsets into clientHeader of the key/value pairs
+ size_t numCommandWords;
+ size_t numQualKeys; // number of qualifier keys we have found, <= maxQualKeys
+ size_t numHeaderKeys; // number of keys we have found, <= maxHeaders
+
+ // HTTP sessions
+ struct HttpSession
+ {
+ uint32_t ip;
+ uint32_t lastQueryTime;
+ bool isPostUploading;
+ uint16_t postPort;
+ };
+
+ HttpSession sessions[maxHttpSessions];
+ uint8_t numSessions;
+ uint8_t clientsServed;
+
+ bool Authenticate();
+ bool IsAuthenticated() const;
+ void UpdateAuthentication();
+ bool RemoveAuthentication();
+ const char* GetKeyValue(const char *key) const; // return the value of the specified key, or nullptr if not present
+
+ // Deal with incoming G-Codes
+
+ char gcodeBuffer[gcodeBufferLength];
+ uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
+
+ void LoadGcodeBuffer(const char* gc);
+ void ProcessGcode(const char* gc);
+ void StoreGcodeData(const char* data, uint16_t len);
+
+ // Responses from GCodes class
+
+ uint32_t seq; // Sequence number for G-Code replies
+ OutputStack *gcodeReply;
+
+ // File uploads
+ uint32_t postFileLength, uploadedBytes; // How many POST bytes do we expect and how many have already been written?
+ time_t fileLastModified;
+
+ // Deferred requests (rr_fileinfo)
+ volatile Connection deferredRequestConnection; // Which connection expects a response for a deferred request?
+ char filenameBeingProcessed[FILENAME_LENGTH]; // The filename being processed (for rr_fileinfo)
+
+ void ProcessDeferredRequest();
+ };
+ HttpInterpreter *httpInterpreter;
+
+ class FtpInterpreter : public ProtocolInterpreter
+ {
+ public:
+
+ FtpInterpreter(Platform *p, Webserver *ws, Network *n);
+ void Diagnostics(MessageType mtype) override;
+
+ void ConnectionEstablished() override;
+ void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
+ bool CharFromClient(const char c) override;
+ void ResetState();
+
+ bool DoingFastUpload() const override;
+
+ private:
+
+ enum FtpState
+ {
+ idle, // no client connected
+ authenticating, // not logged in
+ authenticated, // logged in
+ waitingForPasvPort, // waiting for connection to be established on PASV port
+ pasvPortConnected, // client connected to PASV port, ready to send data
+ doingPasvIO // client is connected and data is being transferred
+ };
+ FtpState state;
+ uint8_t connectedClients;
+
+ char clientMessage[ftpMessageLength];
+ size_t clientPointer;
+
+ char filename[FILENAME_LENGTH];
+ char currentDir[FILENAME_LENGTH];
+
+ uint32_t portOpenTime;
+
+ void ProcessLine();
+ void SendReply(int code, const char *message, bool keepConnection = true);
+ void SendFeatures();
+
+ void ReadFilename(uint16_t start);
+ void ChangeDirectory(const char *newDirectory);
+ };
+ FtpInterpreter *ftpInterpreter;
+
+ class TelnetInterpreter : public ProtocolInterpreter
+ {
+ public:
+
+ TelnetInterpreter(Platform *p, Webserver *ws, Network *n);
+ void Diagnostics(MessageType mtype) override;
+
+ void ConnectionEstablished() override;
+ void ConnectionLost(Connection conn /*const ConnectionState *cs*/) override;
+ bool CanParseData() override;
+ bool CharFromClient(const char c) override;
+ void ResetState();
+
+ bool GCodeAvailable() const;
+ char ReadGCode();
+ void HandleGCodeReply(OutputBuffer *reply);
+ void HandleGCodeReply(const char *reply);
+ uint16_t GetGCodeBufferSpace() const;
+
+ void SendGCodeReply();
+
+ private:
+
+ enum TelnetState
+ {
+ idle, // not connected
+ justConnected, // not logged in, but the client has just connected
+ authenticating, // not logged in
+ authenticated // logged in
+ };
+ TelnetState state;
+ uint8_t connectedClients;
+ uint32_t connectTime;
+
+ bool processNextLine;
+ char clientMessage[GCODE_LENGTH];
+ size_t clientPointer;
+
+ bool ProcessLine();
+
+ // Deal with incoming G-Codes
+
+ char gcodeBuffer[gcodeBufferLength];
+ uint16_t gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
+
+ void ProcessGcode(const char* gc);
+ void StoreGcodeData(const char* data, uint16_t len);
+
+ // Converted response from GCodes class (NL -> CRNL)
+
+ OutputBuffer * volatile gcodeReply;
+ };
+ TelnetInterpreter *telnetInterpreter;
+
+ private:
+
+ Platform* platform;
+ Network* network;
+ bool webserverActive;
+ NetworkTransaction *currentTransaction;
+ volatile Connection readingConnection;
+
+ float longWait;
+};
+
+inline bool ProtocolInterpreter::CanParseData() { return true; }
+inline bool ProtocolInterpreter::DoingFastUpload() const { return false; }
+inline bool ProtocolInterpreter::IsUploading() const { return uploadState != notUploading; }
+
+inline uint32_t Webserver::GetReplySeq() const { return httpInterpreter->GetReplySeq(); }
+
+inline uint16_t Webserver::HttpInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
+inline bool Webserver::HttpInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
+inline uint32_t Webserver::HttpInterpreter::GetReplySeq() const { return seq; }
+
+inline uint16_t Webserver::TelnetInterpreter::GetGCodeBufferSpace() const { return (gcodeReadIndex - gcodeWriteIndex - 1u) % gcodeBufferLength; }
+inline bool Webserver::TelnetInterpreter::GCodeAvailable() const { return gcodeReadIndex != gcodeWriteIndex; }
+
+#endif