[pws] [FIX] HTTP Post

Lex Spoon lex at cc.gatech.edu
Sun Mar 5 13:47:34 UTC 2000

In fact Squeak appears to be doing it mostly right, so let's be careful.
  I just dug this info up, and here it is for people who have lives and
thus have other things to do on Spring Break.  :)

First things first.  RFC 2616, "Hypertext Transfer Protocol --
HTTP/1.1", says the following:

      Note: When automatically redirecting a POST request after
      receiving a 301 status code, some existing HTTP/1.0 user agents
      will erroneously change it into a GET request.

So always-use-GET isn't right, unless WWW protocols hawe just gotten
more twisted than ever.  As a side issue on this, consider this nearby

   If the 301 status code is received in response to a request other
   than GET or HEAD, the user agent MUST NOT automatically redirect the
   request unless it can be confirmed by the user, since this might
   change the conditions under which the request was issued.

Thus you're supposed to ask the user before redirecting a POST.  I've
never seen such a query out of Netscape, but Lynx seems to go along with
it.  You can read about it at:


Finally, the RFC does provide a specific method for redirect-using-GET,
and that is response code 303.  Here that one case that Squeak gets
wrong: it treats 303 and all other 3xx responses identically.  Instead,
Squeak should continue to redirect the basic 301, 302, and 307 responses
as POST's (after asking the user, ideally), but it should be changed to
redirect 303 as a GET.  While we're at it, all other 3xx responses than
these four should just generate an error--each code has a different
meaning, and doing a simple redirect doesn't make sense.


PS -- a tangential issue is the manner that HTTP errors are handled in
Squeak right now.  Currently, an error results either in a debugger or
in returning a String instead of a MimeDocument.  IMNSHO this is really
ugly.  Now that we have exceptions, I'm sure there is a better way. 
Furthermore, Url>>#retrieveContents has a similar problem, in that there
is no way to notify the caller of errors at all.  Exceptions should help
there, as well.

PPS -- as another tangential issue, there should probably be a
HTTPRequest object that accumulates parameters, instead of having one
method with millions of parameters, and then adding various permutations
of that method.  This would also make it less painful to add new,
optional parameters, such as a mechanism for querying the "user".

Michael Rueger <Michael.Rueger.-ND at disney.com> wrote:
> This is a multi-part message in MIME format.
> --------------FDD43B2706A10B483AB3AAB2
> Content-Type: text/plain; charset=us-ascii
> Content-Transfer-Encoding: 7bit
> Hi all,
> the attached filein fixes a problem with the handling of redirects in the
> http post document method.
> The original implementation issued a post to the received redirection address
> instead of a get.
> Using the get to my understanding is still not completely compliant with the
> RFC, but browsers seem to behave that way.
> Does anybody have a more substantiated answer to this problem?
> Michael
> -- 
>  "To improve is to change, to be perfect is to change often." 
>                                             Winston Churchill
> +------------------------------------------------------------+
> | Michael Rueger                                             |
> | Phone: ++1 (818) 623 3283        Fax:   ++1 (818) 623 3559 |
> +---------- Michael.Rueger.-ND at corp.go.com ------------------+
> --------------FDD43B2706A10B483AB3AAB2
> Content-Type: text/plain; charset=us-ascii;
>  name="HTTPSocket class-httpPostDocumentargsacceptrequest.st"
> Content-Transfer-Encoding: 7bit
> Content-Disposition: inline;
>  filename="HTTPSocket class-httpPostDocumentargsacceptrequest.st"
> 'From ''Squeak2.8alpha'' of 26 January 2000 update 1855 [latest update: #''Squeak2.8alpha'' of 26 January 2000 update 1855] on 2 March 2000 at 4:14:53 pm'!
> !HTTPSocket class methodsFor: 'get the page' stamp: 'mir 3/2/2000 16:14'!
> httpPostDocument: url  args: argsDict accept: mimeType request: requestString
> 	"like httpGET, except it does a POST instead of a GET.  POST allows data to be uploaded"
> 	| s header length page list firstData aStream argsStream first type newUrl httpUrl |
> 	Socket initializeNetwork.
> 	httpUrl _ Url absoluteFromText: url.
> 	page _ httpUrl toText.
> 	"add arguments"
> 	argsDict ifNotNil: [page _ page, (self argString: argsDict) ].
> 	"encode the arguments dictionary"
> 	argsStream _ WriteStream on: String new.
> 	first _ true.
> 	argsDict associationsDo: [ :assoc |
> 		assoc value do: [ :value |
> 			first ifTrue: [ first _ false ] ifFalse: [ argsStream nextPut: $& ].
> 			argsStream nextPutAll: assoc key encodeForHTTP.
> 			argsStream nextPut: $=.
> 			argsStream nextPutAll: value encodeForHTTP.
> 	] ].
> 	s _ HTTPSocket new. 
> 	s _ self initHTTPSocket: httpUrl wait: (self deadlineSecs: 30) ifError: [:errorString | ^errorString].
> 	Transcript cr; show: url; cr.
> 	s sendCommand: 'POST ', page, ' HTTP/1.0', CrLf, 
> 		(mimeType ifNotNil: ['ACCEPT: ', mimeType, CrLf] ifNil: ['']),
> 		'ACCEPT: text/html', CrLf,	"Always accept plain text"
> 		HTTPBlabEmail,	"may be empty"
> 		requestString,	"extra user request. Authorization"
> 		'User-Agent: Squeak 1.31', CrLf,
> 		'Content-type: application/x-www-form-urlencoded', CrLf,
> 		'Content-length: ', argsStream contents size printString, CrLf,
> 		'Host: ', httpUrl authority, CrLf.  "blank line automatically added"
> 	s sendCommand: argsStream contents.
> 	"get the header of the reply"
> 	list _ s getResponseUpTo: CrLf, CrLf.	"list = header, CrLf, CrLf, beginningOfData"
> 	header _ list at: 1.
> 	"Transcript show: page; cr; show: argsStream contents; cr; show: header; cr."
> 	firstData _ list at: 3.
> 	"dig out some headers"
> 	s header: header.
> 	length _ s getHeader: 'content-length'.
> 	length ifNotNil: [ length _ length asNumber ].
> 	type _ s getHeader: 'content-type'.
> 	s responseCode first = $3 ifTrue: [
> 		newUrl _ s getHeader: 'location'.
> 		newUrl ifNotNil: [
> 			Transcript show: 'Response: ' , s responseCode.
> 			Transcript show: ' redirecting to: ', newUrl; cr.
> 			s destroy.
> 			"^self httpPostDocument: newUrl  args: argsDict  accept: mimeType"
> 			^self httpGetDocument: newUrl accept: mimeType ] ].
> 	aStream _ s getRestOfBuffer: firstData totalLength: length.
> 	s responseCode = '401' ifTrue: [^ header, aStream contents].
> 	s destroy.	"Always OK to destroy!!"
> 	^ MIMEDocument contentType: type  content: aStream contents url: url! !
> --------------FDD43B2706A10B483AB3AAB2--

More information about the Squeak-dev mailing list