Webserver coming along. Some tidying. For some reason there is a

big delay on closing the connection when there is a second partly-full
buffer of data sent to the browser after a completely full one.
This commit is contained in:
Adrian Bowyer 2013-10-02 12:31:38 +01:00
parent 7d3016a7f3
commit 6686e23f4d
4 changed files with 186 additions and 135 deletions

View file

@ -681,6 +681,10 @@ void Platform::Spin()
// } // }
} }
//*************************************************************************************************
// Serial/USB class
Line::Line() Line::Line()
{ {
} }
@ -693,47 +697,70 @@ void Line::Init()
while (!SerialUSB.available()); while (!SerialUSB.available());
} }
//***************************************************************************************************
// Network/Ethernet class
// C calls to interface with LWIP (http://savannah.nongnu.org/projects/lwip/)
// These are implemented in, and called from, a modified version of httpd.c
// in the network directory.
extern "C" extern "C"
{ {
static char* hdat = 0; // Transmit data to the Network
static int hlen = 0;
//static float nwtime = 100000.0;
void SetNetworkDataToSend(char* data, int length); void SetNetworkDataToSend(char* data, int length);
// Close the connection
void CloseConnection(); void CloseConnection();
void NWSetNetworkDataToSend(char* data, int length) // Called to put out a message via the RepRap firmware.
{
hdat = data;
hlen = length;
// nwtime = reprap.GetPlatform()->Time() + 3.0;
}
void RepRapNetworkMessage(char* s) void RepRapNetworkMessage(char* s)
{ {
reprap.GetPlatform()->Message(HOST_MESSAGE, s); reprap.GetPlatform()->Message(HOST_MESSAGE, s);
} }
// Called to push data into the RepRap firmware.
void RepRapNetworkReceiveInput(char* ip, int length) void RepRapNetworkReceiveInput(char* ip, int length)
{ {
reprap.GetPlatform()->GetNetwork()->ReceiveInput(ip, length); reprap.GetPlatform()->GetNetwork()->ReceiveInput(ip, length);
} }
// Called when transmission of outgoing data is complete to allow
// the RepRap firmware to write more.
void RepRapNetworkAllowWriting() void RepRapNetworkAllowWriting()
{ {
reprap.GetPlatform()->GetNetwork()->SetWriteEnable(true); reprap.GetPlatform()->GetNetwork()->SetWriteEnable(true);
} }
// Called by the RepRap firmware to transmit data, if there is
// any to send.
void SendDataFromRepRapNetwork() void SendDataFromRepRapNetwork()
{ {
if(!reprap.GetPlatform()->GetNetwork()->DataToSendAvailable()) if(!reprap.GetPlatform()->GetNetwork()->DataToSendAvailable())
return; return;
// Stop the generation of more data.
reprap.GetPlatform()->GetNetwork()->SetWriteEnable(false); reprap.GetPlatform()->GetNetwork()->SetWriteEnable(false);
// Find where the data is.
char* data = reprap.GetPlatform()->GetNetwork()->OutputBuffer(); char* data = reprap.GetPlatform()->GetNetwork()->OutputBuffer();
int length = reprap.GetPlatform()->GetNetwork()->OutputBufferLength(); int length = reprap.GetPlatform()->GetNetwork()->OutputBufferLength();
// Send it.
SetNetworkDataToSend(data, length); SetNetworkDataToSend(data, length);
// Prepare to write more, when writing is re-enabled.
reprap.GetPlatform()->GetNetwork()->ClearWriteBuffer(); reprap.GetPlatform()->GetNetwork()->ClearWriteBuffer();
} }
@ -746,36 +773,47 @@ Network::Network()
ethPinsInit(); ethPinsInit();
} }
void Network::Init() // Reset the network to its disconnected and ready state.
void Network::Reset()
{ {
alternateInput = NULL;
alternateOutput = NULL;
init_ethernet();
inputPointer = 0; inputPointer = 0;
inputLength = -1; inputLength = -1;
outputPointer = 0; outputPointer = 0;
outputLength = -1; outputLength = -1;
writeEnabled = true; writeEnabled = true;
status = nothing;
} }
// Look for CloseConnectionAndFreeBuffer void Network::Init()
{
//static void FreeBuffer(); alternateInput = NULL;
alternateOutput = NULL;
init_ethernet();
Reset();
}
void Network::Spin() void Network::Spin()
{ {
// Finish reading any data that's been received. // Finish reading any data that's been received.
if(inputPointer < inputLength) if(inputPointer < inputLength)
{
//reprap.GetWebserver()->Spin(); // Is this sensible?
return; return;
}
ethernet_task();
// Send any data that's available
SendDataFromRepRapNetwork(); SendDataFromRepRapNetwork();
// Poll the network, and update its timers.
ethernet_task(); ethernet_task();
// If we've finished generating data, queue up the
// last bytes recorded (which may not fill the
// buffer) to send.
if(!reprap.GetWebserver()->WebserverIsWriting()) if(!reprap.GetWebserver()->WebserverIsWriting())
{ {
if(outputPointer > 0) if(outputPointer > 0)
@ -784,33 +822,15 @@ void Network::Spin()
outputPointer = 0; outputPointer = 0;
} }
} }
// if(reprap.GetPlatform()->Time() > nwtime && hdat != 0)
// {
// SetNetworkDataToSend(hdat, hlen);
// hdat = 0;
// }
} }
void Network::ReceiveInput(char* ip, int length) void Network::ReceiveInput(char* ip, int length)
{ {
if(length > STRING_LENGTH) status = clientLive;
{ inputBuffer = ip;
reprap.GetPlatform()->Message(HOST_MESSAGE, "Network input buffer overflow.\n"); inputLength = length;
inputLength = STRING_LENGTH;
} else
inputLength = length;
for(int i = 0; i < inputLength; i++)
{
inputBuffer[i] = ip[i];
//SerialUSB.print(inputBuffer[i]);
}
// inputBuffer = ip;
// inputLength = length;
inputPointer = 0; inputPointer = 0;
//while(Status() != nothing)
// reprap.GetWebserver()->Spin(); // Nasty...
} }
@ -834,6 +854,8 @@ void Network::ClearWriteBuffer()
void Network::Write(char b) void Network::Write(char b)
{ {
// Check for horrible things...
if(!writeEnabled) if(!writeEnabled)
{ {
reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Write(char b) - Attempt to write when disabled.\n"); reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Write(char b) - Attempt to write when disabled.\n");
@ -855,9 +877,13 @@ void Network::Write(char b)
return; return;
} }
// Add the byte to the buffer
outputBuffer[outputPointer] = b; outputBuffer[outputPointer] = b;
outputPointer++; outputPointer++;
// Buffer full? If so, flag it to send.
if(outputPointer >= STRING_LENGTH - 5) // 5 is for safety if(outputPointer >= STRING_LENGTH - 5) // 5 is for safety
{ {
outputLength = outputPointer; outputLength = outputPointer;
@ -866,6 +892,9 @@ void Network::Write(char b)
outputLength = -1; outputLength = -1;
} }
// If outputLength has been set, there's some data ready
// to send.
bool Network::DataToSendAvailable() bool Network::DataToSendAvailable()
{ {
return (outputLength > 0); return (outputLength > 0);
@ -879,6 +908,9 @@ bool Network::CanWrite()
void Network::SetWriteEnable(bool enable) void Network::SetWriteEnable(bool enable)
{ {
writeEnabled = enable; writeEnabled = enable;
// Reset the write buffer if needs be.
if(writeEnabled && outputLength > 0) if(writeEnabled && outputLength > 0)
{ {
outputLength = -1; outputLength = -1;
@ -886,6 +918,11 @@ void Network::SetWriteEnable(bool enable)
} }
} }
// This is not called for data, only for internally-
// generated short strings at the start of a transmission,
// so it should never overflow the buffer (which is checked
// anyway).
void Network::Write(char* s) void Network::Write(char* s)
{ {
int i = 0; int i = 0;
@ -909,19 +946,14 @@ bool Network::Read(char& b)
void Network::Close() void Network::Close()
{ {
CloseConnection(); CloseConnection();
// if (client) Reset();
// {
// client.stop();
// //Serial.println("client disconnected");
// } else
// reprap.GetPlatform()->Message(HOST_MESSAGE, "Attempt to disconnect non-existent client.");
} }
int8_t Network::Status() int8_t Network::Status()
{ {
if(inputPointer >= inputLength) if(inputPointer >= inputLength)
return nothing; return status;
return clientConnected | byteAvailable; return status | clientConnected | byteAvailable;
} }

View file

@ -252,12 +252,16 @@ protected:
Network(); Network();
void Init(); void Init();
void Spin(); void Spin();
private: private:
char inputBuffer[STRING_LENGTH];
void Reset();
char* inputBuffer;
char outputBuffer[STRING_LENGTH]; char outputBuffer[STRING_LENGTH];
int inputPointer, inputLength; int inputPointer, inputLength;
int outputPointer, outputLength; int outputPointer, outputLength;
bool writeEnabled; bool writeEnabled;
int8_t status;
}; };
// This class handles serial I/O - typically via USB // This class handles serial I/O - typically via USB

View file

@ -636,12 +636,14 @@ void Webserver::Spin()
} }
} }
// if (platform->GetNetwork()->Status() & clientLive) if (platform->GetNetwork()->Status() & clientLive)
{ {
if(needToCloseClient) if(needToCloseClient)
{ {
if(platform->Time() - clientCloseTime < CLIENT_CLOSE_DELAY || !platform->GetNetwork()->CanWrite()) if(platform->Time() - clientCloseTime < CLIENT_CLOSE_DELAY || !platform->GetNetwork()->CanWrite())
return; return;
//if(!platform->GetNetwork()->CanWrite())
// return;
needToCloseClient = false; needToCloseClient = false;
platform->GetNetwork()->Close(); platform->GetNetwork()->Close();
} }

View file

@ -30,6 +30,17 @@
* *
*/ */
/*
* Heavily modified by Adrian
*
* RepRapPro Ltd
* http://reprappro.com
*
* 2 October 2013
*
*/
//#include "lwipopts.h" //#include "lwipopts.h"
//#if defined(HTTP_RAW_USED) //#if defined(HTTP_RAW_USED)
// //
@ -50,20 +61,31 @@
#include "lwip/src/include/lwip/tcp.h" #include "lwip/src/include/lwip/tcp.h"
#include "fs.h" #include "fs.h"
void RepRapNetworkReceiveInput(char* ip, int length);
void RepRapNetworkMessage(char* s);
void RepRapNetworkAllowWriting();
//void NWSetNetworkDataToSend(char* data, int length);
struct http_state { struct http_state {
char *file; char *file;
u16_t left; u16_t left;
u8_t retries; u8_t retries;
}; };
// Prototypes for the RepRap functions in Platform.cpp that we
// need to call.
void RepRapNetworkReceiveInput(char* ip, int length);
void RepRapNetworkMessage(char* s);
void RepRapNetworkAllowWriting();
// Static storage for pointers that need to be saved when we go
// out to the RepRap firmware for when it calls back in again.
// Note that this means that the code is not reentrant, but in
// our context it doesn't need to be.
static struct tcp_pcb* activePcb; static struct tcp_pcb* activePcb;
static struct tcp_pcb* pcbToClose = 0; static struct tcp_pcb* pcbToClose = 0;
static struct http_state* activeHttpState; static struct http_state* activeHttpState;
static struct pbuf* pbufToFree = 0;
static struct tcp_pcb* sendingPcb = 0;
static int initCount = 0;
bool alreadySending = false;
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
static void static void
@ -78,33 +100,38 @@ conn_err(void *arg, err_t err)
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
// Added to allow RepRap to close the connection.
void CloseConnection() void CloseConnection()
{ {
RepRapNetworkMessage("CloseConnection() called.\n");
if(pcbToClose == 0) if(pcbToClose == 0)
return; return;
RepRapNetworkMessage("Got a pcb.\n"); RepRapNetworkMessage("CloseConnection() called.\n");
tcp_arg(pcbToClose, NULL);
tcp_arg(pcbToClose, NULL); tcp_sent(pcbToClose, NULL);
tcp_sent(pcbToClose, NULL); tcp_recv(pcbToClose, NULL);
tcp_recv(pcbToClose, NULL); //mem_free(hs);
//mem_free(hs); tcp_close(pcbToClose);
tcp_close(pcbToClose); pcbToClose = 0;
pcbToClose = 0; alreadySending = false;
} }
// httpd.c's close function, slightly mashed...
static void static void
close_conn(struct tcp_pcb *pcb, struct http_state *hs) close_conn(struct tcp_pcb *pcb, struct http_state *hs)
{ {
RepRapNetworkMessage("Internal close_conn called.\n"); RepRapNetworkMessage("Internal close_conn called.\n");
// CloseConnection();
tcp_arg(pcb, NULL); tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL); tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL); tcp_recv(pcb, NULL);
//mem_free(hs); //mem_free(hs);
tcp_close(pcb); tcp_close(pcb);
alreadySending = false;
} }
char scratch[40];
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
static void static void
send_data(struct tcp_pcb *pcb, struct http_state *hs) send_data(struct tcp_pcb *pcb, struct http_state *hs)
@ -120,6 +147,11 @@ send_data(struct tcp_pcb *pcb, struct http_state *hs)
len = hs->left; len = hs->left;
} }
RepRapNetworkMessage("Sending ");
sprintf(scratch, "%d", len);
RepRapNetworkMessage(scratch);
RepRapNetworkMessage("..");
do { do {
err = tcp_write(pcb, hs->file, len, 0); err = tcp_write(pcb, hs->file, len, 0);
if (err == ERR_MEM) { if (err == ERR_MEM) {
@ -158,25 +190,33 @@ http_poll(void *arg, struct tcp_pcb *pcb)
return ERR_OK; return ERR_OK;
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
static err_t static err_t
http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{ {
struct http_state *hs; struct http_state *hs;
//RepRapNetworkAllowWriting();
LWIP_UNUSED_ARG(len); LWIP_UNUSED_ARG(len);
hs = arg; hs = arg;
hs->retries = 0; hs->retries = 0;
if (hs->left > 0) { RepRapNetworkMessage("..sent\n");
if (hs->left > 0)
{
send_data(pcb, hs); send_data(pcb, hs);
} else { } else
{
// See if there is more to send, and remember the
// pcb for when the connection is closed.
// TODO - possible memory leak?
RepRapNetworkAllowWriting(); RepRapNetworkAllowWriting();
sendingPcb = pcb;
pcbToClose = pcb; pcbToClose = pcb;
tcp_sent(pcb, http_sent);
//close_conn(pcb, hs); //close_conn(pcb, hs);
} }
@ -184,8 +224,10 @@ http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
static struct pbuf* pbufToFree = 0; // ReoRap calls this with data to send.
static struct tcp_pcb* sendingPcb = 0; // It has the side effect of freeing the input buffer
// that prompted the transmission, as that must now have been fully read.
// If RepRap ignores input, is this another potential memory leak?
void SetNetworkDataToSend(char* data, int length) void SetNetworkDataToSend(char* data, int length)
{ {
@ -202,76 +244,45 @@ void SetNetworkDataToSend(char* data, int length)
send_data(sendingPcb, activeHttpState); send_data(sendingPcb, activeHttpState);
if(alreadySending)
return;
/* Tell TCP that we wish be to informed of data that has been /* Tell TCP that we wish be to informed of data that has been
successfully sent by a call to the http_sent() function. */ successfully sent by a call to the http_sent() function. */
tcp_sent(sendingPcb, http_sent); tcp_sent(sendingPcb, http_sent);
alreadySending = true;
} }
static err_t static err_t
http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{ {
int i; int i;
char *data; char *data;
//struct fs_file file;
//struct http_state *hs;
//hs = arg; if (err == ERR_OK && p != NULL)
{
/* Inform TCP that we have taken the data. */
tcp_recved(pcb, p->tot_len);
if (err == ERR_OK && p != NULL) { if (activeHttpState->file == NULL)
{
data = p->payload;
RepRapNetworkReceiveInput(data, p->len);
pbufToFree = p;
sendingPcb = pcb;
} else
{
pbuf_free(p);
}
}
/* Inform TCP that we have taken the data. */ if (err == ERR_OK && p == NULL) {
tcp_recved(pcb, p->tot_len); close_conn(pcb, activeHttpState);
}
if (activeHttpState->file == NULL) { return ERR_OK;
data = p->payload;
RepRapNetworkReceiveInput(data, p->len);
// if (strncmp(data, "GET ", 4) == 0) {
// for(i = 0; i < 40; i++) {
// if (((char *)data + 4)[i] == ' ' ||
// ((char *)data + 4)[i] == '\r' ||
// ((char *)data + 4)[i] == '\n') {
// ((char *)data + 4)[i] = 0;
// }
// }
//
// if (*(char *)(data + 4) == '/' &&
// *(char *)(data + 5) == 0) {
// fs_open("/index.html", &file);
// } else if (!fs_open((char *)data + 4, &file)) {
// fs_open("/404.html", &file);
// }
pbufToFree = p;
sendingPcb = pcb;
//NWSetNetworkDataToSend(file.data, file.len);
// activeHttpState->file = file.data;
// activeHttpState->left = file.len;
// /* printf("data %p len %ld\n", hs->file, hs->left);*/
//
// pbuf_free(pbufToFree);
//
// send_data(sendingPcb, activeHttpState);
//
// /* Tell TCP that we wish be to informed of data that has been
// successfully sent by a call to the http_sent() function. */
// tcp_sent(sendingPcb, http_sent);
// } else {
// pbuf_free(p);
// close_conn(pcb, activeHttpState);
// }
} else {
pbuf_free(p);
}
}
if (err == ERR_OK && p == NULL) {
close_conn(pcb, activeHttpState);
}
return ERR_OK;
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
static err_t static err_t
@ -284,9 +295,7 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
tcp_setprio(pcb, TCP_PRIO_MIN); tcp_setprio(pcb, TCP_PRIO_MIN);
/* Allocate memory for the structure that holds the state of the // Ignore arg; we know it must be our static activeHttpState
connection. */
//hs = (struct http_state *)mem_malloc(sizeof(struct http_state));
hs = activeHttpState; hs = activeHttpState;
@ -315,11 +324,15 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
} }
/*-----------------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------------*/
// This function is called once at the start. // This function (is)x should be called only once at the start.
void void
httpd_init(void) httpd_init(void)
{ {
initCount++;
if(initCount > 1)
RepRapNetworkMessage("httpd_init() called more than once.\n");
activeHttpState = (struct http_state *)mem_malloc(sizeof(struct http_state)); activeHttpState = (struct http_state *)mem_malloc(sizeof(struct http_state));
activePcb = tcp_new(); activePcb = tcp_new();
tcp_bind(activePcb, IP_ADDR_ANY, 80); tcp_bind(activePcb, IP_ADDR_ANY, 80);