How to restart an image running a server?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

How to restart an image running a server?

Mike Anderson-3

Hello All,

I have an HTTP server, and I want to be able to snapshot the image and
have the server come up again when the image is restarted. I have this
partially working, but aspects of it are puzzling me greatly and I'm
hoping that someone can shed some light on it.

I have created a proxy class ExternalResource which wraps all calls to
the NetServers's Socket (held in the inst var. resource). After image
restart I want to nil resource, and then use createBlock to lazily
recreate it.

Object subclass: #ExternalResource
        instanceVariableNames: 'resource createBlock'
        classVariableNames: 'ExternalProcesses'
        ...

ExternalResource >> ensureValid
        resource isNil ifTrue: [ resource := createBlock value ].
        ^resource

ExternalResource >> anyMethodOfSocket
        "Example of wrapper methods."
        ^self ensureValid anyMethodOfSocket

ExternalResource class update: aspect
        aspect == #returnFromSnapshot ifTrue:
            [ self withAllSubclassesDo:
                [ :class | class allInstancesDo:
                    [ :obj | obj invalidate "resource := nil" ] ] ].

However, I find that that doesn't work - I get a backtrace
(backtrace1.log) on restart and the server is not available.

I changed the code so that it suspends the process accessing the socket
before it loops through doing the invalidate code above. Following this,
the server comes back up. I get one backtrace (backtrace2.log), which I
think is the result of the process that was in the middle of serving the
request that caused the snapshot, but otherwise all is well.

ExternalResource >> anyMethodOfSocket
        "Example of wrapper methods."
        ExternalProcesses add: Processor activeProcess.
        [ ^self ensureValid anyMethodOfSocket ]
                ensure:
                [ ExternalProcesses remove: Processor activeProcess. ].

ExternalResource class >> update: aspect
        | ps |
        #returnFromSnapshot = aspect ifFalse: [ ^self ].
        (ps := ExternalProcesses asSet)
                remove: Processor activeProcess ifAbsent: [].
        ps do: [ :each | each suspend ].
        self withAllSubclassesDo:
                [ :class | class allInstancesDo:
                        [ :obj | obj invalidate ] ].
        ps do: [ :each | each resume ].

Pleased with this, I thought it would be neater to suspend the process
*before* the snapshot, to guarantee the sequence of events. However, I
now get a backtrace when I snapshot the image (backtrace3a.log), even
though the server seems to continue running fine. If I restart this
image, I again get a backtrace immediately (backtrace3b.log), but again,
the server seems to be running fine.

ExternalResource class >> update: aspect
        | ps |
        (#(#aboutToSnapshot #finishedSnapshot #returnFromSnapshot)
                includes: aspect) ifFalse: [ ^self ].
        (ps := ExternalProcesses asSet)
                remove: Processor activeProcess ifAbsent: [].
        aspect = #aboutToSnapshot ifTrue:
                [ ps do: [ :each | each suspend ].
                ^self ].
        aspect == #returnFromSnapshot ifTrue:
                [ self withAllSubclassesDo:
                        [ :class | class allInstancesDo:
                                [ :obj | obj invalidate ] ] ].
        ps do: [ :each | each resume ].

I hope I've posted enough code for all this to make sense. There's quite
a lot of code in total, and I don't know how to bundle it up
conveniently, so I've tried to summarize the key methods.

The things confusing me are:

Why do I need to suspend the process at all? Presumably this means that
the process sending update: #returnFromSnapshot is not a privileged
process and other processes are able to get processor time?

Why does my 'neater' version give me a backtrace when I snapshot? Is
there something about suspending that process that is a no-no?

I'd really like to get to the bottom of this.

Mike



 2006-10-03T21:24:54+00:00 <= nil ?
Object: nil error: did not understand #do:
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
CoreException>>#activateOuterHandlerFor:
MessageNotUnderstood(Signal)>>#pass
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
DLD class>>#primDefineExternFunc:
DLD class>>#defineExternFunc:
CFunctionDescriptor class>>#addressOf:
CFunctionDescriptor>>#isValid
CFunctionDescriptor>>#callFrom:into:
TCP.TCPSocketImpl class(TCP.AbstractSocketImpl class)>>#soError
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#soError
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
BlockClosure>>#ensure:
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#fileOp:with:ifFail:
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#ensureWriteable
optimized [] in TCP.Socket>>#newWriteBuffer:
TCP.WriteBuffer>>#flush
TCP.Socket>>#flush
NetClients.CrLfStream>>#nextPut:
NetClients.CrLfStream(Stream)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendResponseType
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendHeader
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#respondTo:
Hector.ReadEvalPrintServlet>>#respondTo:
[] in Hector.HectorServlet>>#respondTo:
True>>#ifTrue:
Hector.HectorServlet>>#respondTo:
NetClients.WikiWorks.CompositeServlet>>#respondTo:
NetClients.WikiWorks.WebServer>>#respondTo:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
Time class>>#millisecondsToRun:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
ProcessorScheduler>>#terminateActive
ContextPart class>>#unwind:
ContextPart class>>#unwind
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
CoreException>>#activateOuterHandlerFor:
MessageNotUnderstood(Signal)>>#pass
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
DLD class>>#primDefineExternFunc:
DLD class>>#defineExternFunc:
CFunctionDescriptor class>>#addressOf:
CFunctionDescriptor>>#isValid
CFunctionDescriptor>>#callFrom:into:
TCP.TCPSocketImpl class(TCP.AbstractSocketImpl class)>>#soError
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#soError
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
BlockClosure>>#ensure:
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#fileOp:with:ifFail:
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#ensureWriteable
optimized [] in TCP.Socket>>#newWriteBuffer:
TCP.WriteBuffer>>#flush
TCP.Socket>>#flush
NetClients.CrLfStream>>#nextPut:
NetClients.CrLfStream(Stream)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendResponseType
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendHeader
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#respondTo:
Hector.ReadEvalPrintServlet>>#respondTo:
[] in Hector.HectorServlet>>#respondTo:
True>>#ifTrue:
Hector.HectorServlet>>#respondTo:
NetClients.WikiWorks.CompositeServlet>>#respondTo:
NetClients.WikiWorks.WebServer>>#respondTo:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
Time class>>#millisecondsToRun:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
(ip 6)ProcessorScheduler>>#yield
(ip 12)[] in Delay class>>#initialize
(ip 16)[] in Process>>#onBlock:at:suspend:
(ip 10)BlockClosure>>#on:do:
(ip 14)[] in Process>>#onBlock:at:suspend:
(ip 14)BlockContext class>>#fromClosure:parent:

Processing package file: hector.xml
Hector Processing package file: hector.xml
Hector Processing package file: /home/mike/smalltalk/packages/packages.xml
MUtility ExternalResources NaiveXML PackageUtils PackageInstaller CLibraries XLib Imlib2 Cairo Applets DBI DBD-libmysqlclient DBD-libpq Loading package ExternalResources
Loading package Hector
Loading package NaiveXML
 2006-10-03T21:15:55+00:00 <=  2006-10-03T21:15:37+00:00 ?
7 GET OrderedCollection ('Hector' 'ReadEvalPrint' )
 2006-08-28T14:42:22+00:00 <=  2006-08-28T14:42:22+00:00 ?
4 GET OrderedCollection ('res' 'hector.css' )
 2006-06-10T18:40:04+00:00 <=  2006-06-10T18:40:04+00:00 ?
4 GET OrderedCollection ('res' 'sarissa' 'sarissa.js' )
 2006-08-31T00:38:54+00:00 <=  2006-08-31T00:38:54+00:00 ?
3 GET OrderedCollection ('res' 'hector.js' )
Object: nil error: did not understand #isPeerAlive
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
NetClients.WikiWorks.WebRequest>>#stream:
NetClients.WikiWorks.WebRequest>>#initConnection:
NetClients.WikiWorks.WebRequest class>>#for:
NetClients.WikiWorks.WebSession>>#next
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
ProcessorScheduler>>#terminateActive
ContextPart class>>#unwind:
ContextPart class>>#unwind
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
NetClients.WikiWorks.WebRequest>>#stream:
NetClients.WikiWorks.WebRequest>>#initConnection:
NetClients.WikiWorks.WebRequest class>>#for:
NetClients.WikiWorks.WebSession>>#next
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
 2006-10-03T21:16:00+00:00 <= nil ?
129 POST OrderedCollection ('Hector' 'ReadEvalPrint' )
(ip 6)ProcessorScheduler>>#yield
(ip 12)[] in Delay class>>#initialize
(ip 16)[] in Process>>#onBlock:at:suspend:
(ip 10)BlockClosure>>#on:do:
(ip 14)[] in Process>>#onBlock:at:suspend:
(ip 14)BlockContext class>>#fromClosure:parent:

Object: nil error: did not understand #isPeerAlive
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
NetClients.WikiWorks.WebRequest>>#stream:
NetClients.WikiWorks.WebRequest>>#initConnection:
NetClients.WikiWorks.WebRequest class>>#for:
NetClients.WikiWorks.WebSession>>#next
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
ProcessorScheduler>>#terminateActive
ContextPart class>>#unwind:
ContextPart class>>#unwind
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
NetClients.WikiWorks.WebRequest>>#stream:
NetClients.WikiWorks.WebRequest>>#initConnection:
NetClients.WikiWorks.WebRequest class>>#for:
NetClients.WikiWorks.WebSession>>#next
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
 2006-10-03T21:18:51+00:00 <= nil ?
-2938 POST OrderedCollection ('Hector' 'ReadEvalPrint' )
(ip 6)ProcessorScheduler>>#yield
(ip 12)[] in Delay class>>#initialize
(ip 16)[] in Process>>#onBlock:at:suspend:
(ip 10)BlockClosure>>#on:do:
(ip 14)[] in Process>>#onBlock:at:suspend:
(ip 14)BlockContext class>>#fromClosure:parent:

 2006-10-03T21:01:26+00:00 <= nil ?
Object: nil error: did not understand #do:
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
CoreException>>#activateOuterHandlerFor:
MessageNotUnderstood(Signal)>>#pass
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
DLD class>>#primDefineExternFunc:
DLD class>>#defineExternFunc:
CFunctionDescriptor class>>#addressOf:
CFunctionDescriptor>>#isValid
CFunctionDescriptor>>#callFrom:into:
TCP.TCPSocketImpl class(TCP.AbstractSocketImpl class)>>#soError
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#soError
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
BlockClosure>>#ensure:
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#fileOp:with:ifFail:
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#ensureWriteable
optimized [] in TCP.Socket>>#newWriteBuffer:
TCP.WriteBuffer>>#flush
TCP.Socket>>#flush
NetClients.CrLfStream>>#nextPut:
NetClients.CrLfStream(Stream)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendResponseType
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendHeader
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#respondTo:
Hector.ReadEvalPrintServlet>>#respondTo:
[] in Hector.HectorServlet>>#respondTo:
True>>#ifTrue:
Hector.HectorServlet>>#respondTo:
NetClients.WikiWorks.CompositeServlet>>#respondTo:
NetClients.WikiWorks.WebServer>>#respondTo:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
Time class>>#millisecondsToRun:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
ProcessorScheduler>>#terminateActive
ContextPart class>>#unwind:
ContextPart class>>#unwind
UndefinedObject(Object)>>#primError:
MessageNotUnderstood(Exception)>>#defaultAction
optimized [] in Exception class>>#coreException
MessageNotUnderstood(Signal)>>#activateHandler:
CoreException>>#activateOuterHandlerFor:
MessageNotUnderstood(Signal)>>#pass
optimized [] in NetClients.NetSession>>#run
MessageNotUnderstood(Signal)>>#activateHandler:
MessageNotUnderstood(Exception)>>#signal
UndefinedObject(Object)>>#doesNotUnderstand:
DLD class>>#primDefineExternFunc:
DLD class>>#defineExternFunc:
CFunctionDescriptor class>>#addressOf:
CFunctionDescriptor>>#isValid
CFunctionDescriptor>>#callFrom:into:
TCP.TCPSocketImpl class(TCP.AbstractSocketImpl class)>>#soError
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#soError
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
BlockClosure>>#ensure:
optimized [] in TCP.AbstractSocketImpl>>#ensureWriteable
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#fileOp:with:ifFail:
TCP.TCPSocketImpl(TCP.AbstractSocketImpl)>>#ensureWriteable
optimized [] in TCP.Socket>>#newWriteBuffer:
TCP.WriteBuffer>>#flush
TCP.Socket>>#flush
NetClients.CrLfStream>>#nextPut:
NetClients.CrLfStream(Stream)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#nl
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendResponseType
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#sendHeader
Hector.RepResultResponse(NetClients.WikiWorks.WebResponse)>>#respondTo:
Hector.ReadEvalPrintServlet>>#respondTo:
[] in Hector.HectorServlet>>#respondTo:
True>>#ifTrue:
Hector.HectorServlet>>#respondTo:
NetClients.WikiWorks.CompositeServlet>>#respondTo:
NetClients.WikiWorks.WebServer>>#respondTo:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
Time class>>#millisecondsToRun:
[] in NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
BlockClosure>>#on:do:
NetClients.WikiWorks.WebSession(NetClients.NetSession)>>#run
optimized [] in NetClients.NetThread>>#startNewProcess
[] in Process>>#onBlock:at:suspend:
BlockClosure>>#on:do:
[] in Process>>#onBlock:at:suspend:
BlockContext class>>#fromClosure:parent:
run-hector.st:3: Interrupt
(ip 6)ProcessorScheduler>>#yield
(ip 12)[] in Delay class>>#initialize
(ip 16)[] in Process>>#onBlock:at:suspend:
(ip 10)BlockClosure>>#on:do:
(ip 14)[] in Process>>#onBlock:at:suspend:
(ip 14)BlockContext class>>#fromClosure:parent:

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: How to restart an image running a server?

Paolo Bonzini
The short answer is: I don't have a clue, but I have a feeling that the
attached patch might fix it (just by grepping the sources for senders of
isPeerAlive) -- or a similar patch for your classes.

Looks like you have something to contribute, haven't you?
*cat-from-shrek2-look*

Paolo

--- orig/net/NetServer.st
+++ mod/net/NetServer.st
@@ -100,6 +100,10 @@ startNewProcess
 
     process resume!
 
+isPeerAlive
+    ^socket notNil and: [ socket isPeerAlive ]
+!
+
 socket
     ^socket
 ! !
@@ -240,13 +244,13 @@ run
  ]   on: ExAll
     do: [ :ex |
  "Ignore errors due to bad communication lines."
- self socket isPeerAlive ifFalse: [ ex return ].
+ self isPeerAlive ifFalse: [ ex return ].
  ex pass
     ].
 
  self log: req time: time.
 
- self socket isPeerAlive
+ self isPeerAlive
     ] whileTrue
 !
 

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: How to restart an image running a server?

Mike Anderson-3
Paolo Bonzini wrote:
> The short answer is: I don't have a clue, but I have a feeling that the
> attached patch might fix it (just by grepping the sources for senders of
> isPeerAlive) -- or a similar patch for your classes.

Well, I still have a backtrace, but it's not as nasty-looking as the
last one, so thank you!

> Looks like you have something to contribute, haven't you?
> *cat-from-shrek2-look*

Well, if you put it like that :)

I don't really have much, though. I have a little web server evaluating
expressions submitted from an HTML form, which is nothing that you
couldn't code yourself. Attached is a couple of patches for the
WebServer which I worked out along the way, though. (Looking at them, I
notice there's a FIXME in there, which I know you dislike - sorry)

I was working on an SVG browser initially, inspired by the class
diagrams in "Smalltalk, Objects and Design" - screenshots here:
http://www.friendofthepigeon.co.uk/wordpress/?p=41.

Unfortunately, although Firefox renders them fine (as you can see),
something about rotated text seems to make it ramp up the processor,
which reduces the usability hugely. I thought I'd work on some other
aspects of the system, like getting the server's image to restart.

I suppose I could use horizontal text. Chamond Liu does...

Regards,

Mike


--- old/ContentHandler.st 2006-10-04 20:26:05.000000000 +0000
+++ new/ContentHandler.st 2006-10-04 20:27:42.000000000 +0000
@@ -176,6 +176,7 @@
  ('spl' 'application/futuresplash')
  ('st' 'text/plain') "Of course!"
  ('swf' 'application/x-shockwave-flash')
+ ('svg' 'image/svg+xml')
  ('tar' 'application/x-tar')
  ('tgz' 'application/x-compressed')
  ('tif' 'image/tiff')

--- old/NetServer.st 2006-10-04 19:13:16.000000000 +0000
+++ new/NetServer.st 2006-10-04 19:14:39.000000000 +0000
@@ -134,7 +134,7 @@
 at: port
     | server |
     Servers isNil ifTrue: [ Servers := Dictionary new ].
-    ^Servers at: port ifAbsentPut: [ self new ].
+    ^Servers at: port ifAbsentPut: [ (self new) port: port; yourself ].
 !
 
 initializeServer: port

--- old/WebServer.st 2006-03-26 01:49:09.000000000 +0000
+++ new/WebServer.st 2006-10-04 19:24:39.000000000 +0000
@@ -229,6 +229,12 @@
     self server log: req action uri: req location time: time.
 ! !
 
+Servlet class methodsFor: 'instance creation'!
+
+named: aString
+    ^(self new) name: aString; yourself
+! !
+
 
 !Servlet methodsFor: 'accessing'!
 
@@ -370,7 +376,12 @@
 notModified
     | ifModSince modTime |
     ifModSince := request at: #'IF-MODIFIED-SINCE' ifAbsent: [ nil ].
-    ^ifModSince notNil and: [ self modifiedTime <= ifModSince ]!
+ modTime := self modifiedTime.
+ ifModSince notNil ifTrue:
+ [ "Need equivalent of WebRequest >> #parseTimestamp:"
+ ifModSince := DateTime readFrom:
+ ((ifModSince readStream) skip: 5; yourself). ].
+    ^ifModSince notNil and: [ modTime <= ifModSince ]!
 
 request
     ^request!
@@ -603,10 +614,20 @@
     stream next. "Get nl"
 
     self extractClientData: version.
-    (action sameAs: 'POST') ifTrue: [ self extractPostData: version ].
-    
-    "Get back to binary mode"
-    stream := saveStream!
+
+ (action sameAs: 'POST') ifTrue:
+ [ self
+ extractPostData: version
+ contentLength: (clientData at: #'CONTENT-LENGTH' ifAbsent: [ nil ])].
+
+ "Get back to binary mode"
+    stream := saveStream.!
+
+hasPostData
+    ^postData notEmpty!
+
+postDataAt: aSymbol ifPresent: aBlock
+    ^postData at: aSymbol ifPresent: aBlock!
 
 location
     ^location!
@@ -734,28 +755,39 @@
 
 extractLocation
     uri := (stream upToAll: 'HTTP/') trimSeparators.
-    location := uri substrings: $?.
+    location := uri subStrings: $?.
+ location isEmpty ifTrue:
+ [ self error: 'Empty uri: ', uri, '.' ].
     location size = 2 ifTrue: [ self extractQueryData: (location at: 2) ].
     location := (location at: 1) substrings: $/.
     location := location collect: [:each | (URL decode: each) ].
     location := location reject: [:each | each isEmpty ]!
 
-extractPostData: clientVersion
+extractPostData: clientVersion contentLength: contentLength
+ | s |
     clientVersion ~= '1.0'
- ifTrue: [ stream nextPut: 'HTTP/1.1 100 Continue'; nl; nl ].
+ ifTrue: [ stream nextPutAll: 'HTTP/1.1 100 Continue'; nl; nl ].
 
     (self at: #'CONTENT-TYPE' ifAbsent: [ nil ]) ~=
- 'application/x-www-form-urlencoded' ifTrue: [ ^self ].
-
-    ^self extractQueryData: (stream upTo: Character cr)!
+ 'application/x-www-form-urlencoded' ifTrue: [ ^self ].
+
+ "FIXME: Parse the stream directly, rather than loading it all into
+ memory, because it could be large."
+ s := contentLength notNil
+ ifTrue:
+ [ stream next: contentLength asInteger ]
+ ifFalse:
+ [ stream upTo: Character cr ].
+
+ ^self extractQueryData: s.!
 
 extractQueryData: query
-    (query substrings: $&) do: [ :each || pair |
- pair := each substrings: $=.
- self
-    postDataAt: (URL decode: pair first) asSymbol
-    put: (URL decode: pair last)
-    ]!
+    (query substrings: $&) do:
+ [ :each || pair |
+ pair := each substrings: $=.
+ self
+ postDataAt: (URL decode: pair first) asSymbol
+ put: (URL decode: (pair at: 2 ifAbsent: [ '' ])) ].!
 
 postDataAt: aSymbol put: aValue
     ^postData at: aSymbol put: aValue! !

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: How to restart an image running a server?

Paolo Bonzini

>> The short answer is: I don't have a clue, but I have a feeling that the
>> attached patch might fix it (just by grepping the sources for senders of
>> isPeerAlive) -- or a similar patch for your classes.
>>    
> Well, I still have a backtrace, but it's not as nasty-looking as the
> last one, so thank you!
>  
Can you send it?
> Attached is a couple of patches for the
> WebServer which I worked out along the way, though. (Looking at them, I
> notice there's a FIXME in there, which I know you dislike - sorry)
>  
Nah, let's make it a TODO and I am happy :-)

Paolo



_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: How to restart an image running a server?

Paolo Bonzini

> Nah, let's make it a TODO and I am happy :-)
The real FIXME was the comment on using #parseTimestamp: (a method which
is in fact unused and was *calling* for a wrapper).  So, here is my take
on your patch (only the WebServer.st hunks), which adds and uses a
#dateTimeAt:ifAbsent: method.

Thanks, it was cool to see again a WebServer up and running on Darwin.

Paolo

--- orig/net/httpd/WebServer.st
+++ mod/net/httpd/WebServer.st
@@ -148,7 +148,7 @@ HTTP request using the Basic authenticat
 version
     | number |
     Version isNil ifFalse: [ ^Version ].
-    number := Smalltalk version substrings
+    number := Smalltalk version subStrings
  detect: [ :each | (each at: 1) isDigit ]
  ifNone: [ '0.0' "???" ].
 
@@ -229,6 +229,12 @@ log: req time: time
     self server log: req action uri: req location time: time.
 ! !
 
+Servlet class methodsFor: 'instance creation'!
+
+named: aString
+    ^(self new) name: aString; yourself
+! !
+
 
 !Servlet methodsFor: 'accessing'!
 
@@ -369,8 +375,9 @@ respondTo: aRequest
 
 notModified
     | ifModSince modTime |
-    ifModSince := request at: #'IF-MODIFIED-SINCE' ifAbsent: [ nil ].
-    ^ifModSince notNil and: [ self modifiedTime <= ifModSince ]!
+    ifModSince := request dateTimeAt: #'IF-MODIFIED-SINCE' ifAbsent: [ nil ].
+    modTime := self modifiedTime.
+    ^ifModSince notNil and: [ modTime <= ifModSince ]!
 
 request
     ^request!
@@ -589,6 +596,19 @@ at: aSymbol
 at: aSymbol ifAbsent: aBlock
     ^clientData at: aSymbol ifAbsent: aBlock!
 
+at: aSymbol ifPresent: aBlock
+    ^clientData at: aSymbol ifPresent: aBlock!
+
+dateTimeAt: aSymbol
+    ^self parseTimestamp: (clientData at: aSymbol)!
+
+dateTimeAt: aSymbol ifAbsent: aBlock
+    ^self parseTimestamp: (clientData at: aSymbol ifAbsent: [ ^aBlock value ])!
+
+dateTimeAt: aSymbol ifPresent: aBlock
+    ^clientData at: aSymbol ifPresent: [ :value |
+ aBlock value: (self parseTimestamp: value) ]!
+
 enumeratePostData: aBlock
     postData keysAndValuesDo: aBlock!
 
@@ -603,10 +623,21 @@ getRequest
     stream next. "Get nl"
 
     self extractClientData: version.
-    (action sameAs: 'POST') ifTrue: [ self extractPostData: version ].
-    
+
+    (action sameAs: 'POST') ifTrue: [
+ self
+    extractPostData: version
+    contentLength: (clientData at: #'CONTENT-LENGTH' ifAbsent: [ nil ])
+    ].
+
     "Get back to binary mode"
-    stream := saveStream!
+    stream := saveStream.!
+
+hasPostData
+    ^postData notEmpty!
+
+postDataAt: aSymbol ifPresent: aBlock
+    ^postData at: aSymbol ifPresent: aBlock!
 
 location
     ^location!
@@ -620,35 +651,6 @@ originator
 pageFollows
     WebResponse new respondTo: self!
 
-parseTimestamp: ts
-    | tok d m y time |
-    tok := ts substrings.
-    (tok at: 1) last = $, ifFalse: [ "asctime:  Sun Nov  6 08:49:37 1994"
-         ts size = 5 ifFalse: [ ^nil ].
-         m := (ts at: 2) asSymbol.
-         d := (ts at: 3) asInteger.
-         y := (ts at: 5) asInteger.
-         time := ts at: 4.
-         ^self makeTimestamp: d month: m year: y time: time
-    ].
-    (tok at: 1) size = 4 ifTrue: [ "RFC 822:  Sun, 06 Nov 1994 08:49:37 GMT"
-         ts size = 6 ifFalse: [ ^nil ].
-         d := (ts at: 2) asInteger.
-         m := (ts at: 3) asSymbol.
-         y := (ts at: 4) asInteger.
-         time := ts at: 5.
-         ^self makeTimestamp: d month: m year: y time: time
-    ].
-    "RFC 850 (obsolete):  Sunday, 06-Nov-94 08:49:37 GMT"
-    ts size = 4 ifFalse: [ ^nil ].
-    d := ts at: 2.
-    time := ts at: 3.
-    d size = 9 ifFalse: [ ^nil ].
-    y := (d at: 8) base10DigitValue * 10 + (d at: 9) base10DigitValue + 1900.
-    m := (d copyFrom: 4 to: 6) asSymbol.
-    d := (d at: 1) base10DigitValue * 10 + (d at: 2) base10DigitValue.
-    ^self makeTimestamp: d month: m year: y time: time!
-
 moreRequests
     ^(self at: #Connection) sameAs: 'keep-alive'
 !
@@ -694,20 +696,51 @@ release
 
 !WebRequest methodsFor: 'private'!
 
-at: aSymbol put: aValue
-    ^clientData at: aSymbol put: aValue!
+parseTimestamp: ts
+    | tok d m y time |
+    tok := ts subStrings.
+    (tok at: 1) last = $, ifFalse: [ "asctime:  Sun Nov  6 08:49:37 1994"
+         ts size = 5 ifFalse: [ ^nil ].
+         m := (ts at: 2) asSymbol.
+         d := (ts at: 3) asInteger.
+         y := (ts at: 5) asInteger.
+         time := ts at: 4.
+         ^self makeTimestamp: d month: m year: y time: time
+    ].
+    (tok at: 1) size = 4 ifTrue: [ "RFC 822:  Sun, 06 Nov 1994 08:49:37 GMT"
+         ts size = 6 ifFalse: [ ^nil ].
+         d := (ts at: 2) asInteger.
+         m := (ts at: 3) asSymbol.
+         y := (ts at: 4) asInteger.
+         time := ts at: 5.
+         ^self makeTimestamp: d month: m year: y time: time
+    ].
+    "RFC 850 (obsolete):  Sunday, 06-Nov-94 08:49:37 GMT"
+    ts size = 4 ifFalse: [ ^nil ].
+    d := ts at: 2.
+    time := ts at: 3.
+    d size = 9 ifFalse: [ ^nil ].
+    y := (d at: 8) base10DigitValue * 10 + (d at: 9) base10DigitValue + 1900.
+    m := (d copyFrom: 4 to: 6) asSymbol.
+    d := (d at: 1) base10DigitValue * 10 + (d at: 2) base10DigitValue.
+    ^self makeTimestamp: d month: m year: y time: time!
 
 makeTimestamp: d month: m year: y time: t
-    | month |
+    | month sec |
     t size = 8 ifFalse: [ ^nil ].
 
     month := #(#Jan #Feb #Mar #Apr #May #Jun #Jul #Aug #Sep #Oct #Nov #Dec)
  indexOf: m ifAbsent: [ ^nil ].
 
-    ^(((t at: 1) base10DigitValue * 10 + (t at: 2) base10DigitValue) * 3600)
-     + (((t at: 4) base10DigitValue * 10 + (t at: 5) base10DigitValue) * 60)
-     + (((t at: 7) base10DigitValue * 10 + (t at: 8) base10DigitValue))
-     + (Date newDay: d monthIndex: month year: y) asSeconds!
+    sec :=
+ (((t at: 1) base10DigitValue * 10 + (t at: 2) base10DigitValue) * 3600)
+      + (((t at: 4) base10DigitValue * 10 + (t at: 5) base10DigitValue) * 60)
+      + (((t at: 7) base10DigitValue * 10 + (t at: 8) base10DigitValue)).
+
+    ^(DateTime newDay: d monthIndex: month year: y) addSeconds: sec!
+
+at: aSymbol put: aValue
+    ^clientData at: aSymbol put: aValue!
 
 endOfLine
     ^EndOfLine!
@@ -734,28 +767,36 @@ extractClientData: clientVersion
 
 extractLocation
     uri := (stream upToAll: 'HTTP/') trimSeparators.
-    location := uri substrings: $?.
+    location := uri subStrings: $?.
+ location isEmpty ifTrue:
+ [ self error: 'Empty uri: ', uri, '.' ].
     location size = 2 ifTrue: [ self extractQueryData: (location at: 2) ].
-    location := (location at: 1) substrings: $/.
+    location := (location at: 1) subStrings: $/.
     location := location collect: [:each | (URL decode: each) ].
     location := location reject: [:each | each isEmpty ]!
 
-extractPostData: clientVersion
+extractPostData: clientVersion contentLength: contentLength
+    | s |
     clientVersion ~= '1.0'
- ifTrue: [ stream nextPut: 'HTTP/1.1 100 Continue'; nl; nl ].
+ ifTrue: [ stream nextPutAll: 'HTTP/1.1 100 Continue'; nl; nl ].
 
     (self at: #'CONTENT-TYPE' ifAbsent: [ nil ]) ~=
- 'application/x-www-form-urlencoded' ifTrue: [ ^self ].
-
-    ^self extractQueryData: (stream upTo: Character cr)!
+ 'application/x-www-form-urlencoded' ifTrue: [ ^self ].
+
+    "TODO: Parse the stream directly, rather than loading it all into
+    memory, because it could be large."
+    s := contentLength notNil
+ ifTrue: [ stream next: contentLength asInteger ]
+ ifFalse: [ stream upTo: Character cr ].
+
+    ^self extractQueryData: s!
 
 extractQueryData: query
-    (query substrings: $&) do: [ :each || pair |
- pair := each substrings: $=.
+    (query subStrings: $&) do: [ :each || pair |
+ pair := each subStrings: $=.
  self
     postDataAt: (URL decode: pair first) asSymbol
-    put: (URL decode: pair last)
-    ]!
+    put: (URL decode: (pair at: 2 ifAbsent: [ '' ])) ]!
 
 postDataAt: aSymbol put: aValue
     ^postData at: aSymbol put: aValue! !

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk