HTTPSocket issue

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

HTTPSocket issue

otto
Hello,

I'm trying to make a call using HTTPSocket to a server. In Pharo (1.2)
my response time is sub second but in GemStone (2.4.4.4, with
Squeak-DaleHenrichs.252), I get a response after about 1 minute. The
contents of the response also differs. Here's the example I used:

HTTPSocket
        httpPostDocument: 'http://api-verify.recaptcha.net/verify'
        args: (Dictionary new
                at: 'privatekey' put: #('6LdUKcESAAAAAO8hlc8PyQCRgkS5gXLXTSK4B7b2');
                at: 'remoteip' put: #('');
                at: 'response' put: #('whatever');
                at: 'challenge' put:
#('03AHJ_Vus2ELMgMly7AajxZfgnKUkdOTJWHT2E-OA6pKs16V82abS8oXANUxYn8ziFLs7gRg_Iy3HMVhH8gMzeI8aO3Y7IZJuQDYh7zV8EeIgIjvIclPUCAOtIVblw1mKnf5Mp93tQSABUdeyzwUpgAtgURAiFpFZOAA');
                yourself)
        accept: 'application/octet-stream'
        request: ''

In Pharo, my result is:

'false
incorrect-captcha-sol'

and in GemStone:

'73
false
''Input error: challenge: Error parsing captcha challenge
value\nprivatekey: Format of site key was invalid\n''
0

'

Can you please help?

What I can see is that in HTTPSocket | getRestOfBuffer: we are in
waitForReadDataUpToMs: 5000. But at this point, the buf (fer) already
contains the result above. So, it does not seem to pick up that we've
already read the response.

I would like to mention that I'm also trying to port SoapOpera and got
the SoapClient going in GemStone. I get a quick (and correct) response
in Pharo, but on the GS side it hangs up in the same way. Here's the
Soap test:

((SoapCallEntry tcpHost: 'swikis.ddo.jp' port: 8823) newCall)
                methodName: 'reverseString';
                addParameterNamed: #aString;
                invokeAndReturnWithValues: #(#abc)

Thanks a lot
Otto
Reply | Threaded
Open this post in threaded view
|

Re: HTTPSocket issue

Paul DeBruicker
Hi Otto,

I don't know if this affects your problem but what is sent out the wire
when using HTTPSocket class side methods in Gemstone is different than
in Pharo.  At least that is the case with the
httpGetDocument:args:accept:request: method.  The Gemstone
implementation adds the port number after the host name. e.g.

If you were using httpGetDocument: your call to
'http://api-verify.recaptcha.net/verify' is unchanged in Pharo but
becomes 'http://api-verify.recaptcha.net:80/verify' in Gemstone.  I
don't know if the httpPostDocument: methods do the same or not but if so
that might be causing a problem on the recaptcha.net side that creates
your delays.

I override the Gemstone version of httpGetDocument:args:accept:request:
with this method:

httpGetDocument: url args: args accept: mimeType request: requestString
        "Return the exact contents of a web object. Asks for the given MIME
type. If mimeType is nil, use 'text/html'. An extra requestString may be
submitted and must end with crlf.  The parsed header is saved. Use a
proxy server if one has been registered.  tk 7/23/97 17:12"
        "Note: To fetch raw data, you can use the MIME type
'application/octet-stream'."

        | serverName serverAddr port sock header length bare page list firstData
aStream index connectToHost connectToPort type newUrl portSuffix |
        bare _ (url asLowercase beginsWith: 'http://')
                ifTrue: [url copyFrom: 8 to: url size]
                ifFalse: [url].
        bare _ bare copyUpTo: $#.  "remove fragment, if specified"
        serverName _ bare copyUpTo: $/.
        page _ bare copyFrom: serverName size + 1 to: bare size.
        (serverName includes: $:)
                ifTrue: [ index _ serverName indexOf: $:.
                        port _ (serverName copyFrom: index+1 to: serverName size) asNumber.
                        serverName _ serverName copyFrom: 1 to: index-1.
                        portSuffix:=':', port printString.]
                ifFalse: [ port _ self defaultPort.
                        portSuffix:=''. ].
        page size = 0 ifTrue: [page _ '/'].
        "add arguments"
        args ifNotNil: [page _ page, (self argString: args) ].
        "check for proxy"
        (self shouldUseProxy: serverName)
                ifFalse: [
                        connectToHost := serverName.
                        connectToPort := port]
                ifTrue: [
                        connectToHost := self httpProxyServer.
                        connectToHost isNil ifTrue: [connectToHost := serverName.].
                        connectToPort := self httpProxyPort.
                        page := 'http://', serverName, portSuffix, page. "put back
together"].

        serverAddr _ GsSocket getHostAddressByName: connectToHost.
        serverAddr ifNil: [
                ^ 'Could not resolve the server named: ', connectToHost].

3 timesRepeat: [
        [ sock _ HTTPSocket connectToServerOnHost: serverAddr port:
connectToPort ] on: Error do: [:ex | ^ 'Server ',connectToHost,' is not
responding: ', ex description ].
        sock sendCommand: 'GET ', 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"
                self userAgentString, CrLf,
                'Host: ', serverName,  portSuffix, CrLf. "blank line
automatically added"

        list _ sock getResponseUpTo: CrLf, CrLf ignoring: (String with: CR).
"list = header, CrLf, CrLf,
beginningOfData"
        header _ list at: 1.
        firstData _ list at: 3.
        header isEmpty
                ifTrue: [aStream _ 'server aborted early']
                ifFalse: [
                        "dig out some headers"
                        sock header: header.
                        length _ sock getHeader: 'content-length'.
                        length ifNotNil: [ length _ length asNumber ].
                        type _ sock getHeader: 'content-type'.
                        sock responseCode first = $3 ifTrue: [
                                newUrl _ sock getHeader: 'location'.
                                newUrl ifNotNil: [
                                        sock close.
                                        newUrl _ self expandUrl: newUrl ip: serverAddr port: connectToPort.
                                        ^self httpGetDocument: newUrl args: args  accept: mimeType request:
requestString] ].
                        aStream _ sock getRestOfBuffer: firstData totalLength: length.
                        "a 400-series error"
                        sock responseCode first = $4 ifTrue: [^ header, aStream contents].
                        ].
        sock close. "Always OK to destroy!"
        aStream class ~~ String ifTrue: [
  ^ self mimeDocumentClass contentType: type content: aStream contents
url: url].
        aStream = 'server aborted early' ifTrue: [ ^aStream ].
        ].

(Array with: 'HTTPSocket class>>httpGetDocument:args:accept:request:'
with: aStream with: url) perform: #inspect.

        ^'some other bad thing happened!'


Hope this helps.

Paul.








On 02/21/2011 11:03 AM, Otto Behrens wrote:

> Hello,
>
> I'm trying to make a call using HTTPSocket to a server. In Pharo (1.2)
> my response time is sub second but in GemStone (2.4.4.4, with
> Squeak-DaleHenrichs.252), I get a response after about 1 minute. The
> contents of the response also differs. Here's the example I used:
>
> HTTPSocket
> httpPostDocument: 'http://api-verify.recaptcha.net/verify'
> args: (Dictionary new
> at: 'privatekey' put: #('6LdUKcESAAAAAO8hlc8PyQCRgkS5gXLXTSK4B7b2');
> at: 'remoteip' put: #('');
> at: 'response' put: #('whatever');
> at: 'challenge' put:
> #('03AHJ_Vus2ELMgMly7AajxZfgnKUkdOTJWHT2E-OA6pKs16V82abS8oXANUxYn8ziFLs7gRg_Iy3HMVhH8gMzeI8aO3Y7IZJuQDYh7zV8EeIgIjvIclPUCAOtIVblw1mKnf5Mp93tQSABUdeyzwUpgAtgURAiFpFZOAA');
> yourself)
> accept: 'application/octet-stream'
> request: ''
>
> In Pharo, my result is:
>
> 'false
> incorrect-captcha-sol'
>
> and in GemStone:
>
> '73
> false
> ''Input error: challenge: Error parsing captcha challenge
> value\nprivatekey: Format of site key was invalid\n''
> 0
>
> '
>
> Can you please help?
>
> What I can see is that in HTTPSocket | getRestOfBuffer: we are in
> waitForReadDataUpToMs: 5000. But at this point, the buf (fer) already
> contains the result above. So, it does not seem to pick up that we've
> already read the response.
>
> I would like to mention that I'm also trying to port SoapOpera and got
> the SoapClient going in GemStone. I get a quick (and correct) response
> in Pharo, but on the GS side it hangs up in the same way. Here's the
> Soap test:
>
> ((SoapCallEntry tcpHost: 'swikis.ddo.jp' port: 8823) newCall)
> methodName: 'reverseString';
> addParameterNamed: #aString;
> invokeAndReturnWithValues: #(#abc)
>
> Thanks a lot
> Otto

Reply | Threaded
Open this post in threaded view
|

Re: HTTPSocket issue

otto
Thanks, Paul.

This did not make a difference though. Can you do me a huge favour by
trying the example in your environment? If it works there, I know that
it has something to do with my environment.

Cheers
Otto
Reply | Threaded
Open this post in threaded view
|

Re: HTTPSocket issue

otto
Thanks! Just got it going. Saw that the argString: method on GS was
inlined and behaved differently. Decided to take the Pharo code and
get that to work in GS and with little modification, it did.

Here's the code (on HTTPSocket):

initHTTPSocket: httpUrl timeoutSecs: timeout ifError: aBlock
        ^self initHTTPSocket: httpUrl wait: timeout ifError: aBlock

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 type newUrl httpUrl argString |
        httpUrl := Url absoluteFromText: url.
        page := httpUrl fullPath.
        "add arguments"
        argString := argsDict
                ifNotNil: [
                        argString := self argString: argsDict.
                        argString first = $? ifTrue: [ argString := argString copyFrom: 2
to: argString size]]
                ifNil: [''].

        s := HTTPSocket new.
        s := self initHTTPSocket: httpUrl timeoutSecs: 30 ifError:
[:errorString | ^errorString].
        s sendCommand: 'POST ', page, ' HTTP/1.0', String crlf,
                (mimeType ifNotNil: ['ACCEPT: ', mimeType, String crlf] ifNil: ['']),
                'ACCEPT: text/html', String crlf, "Always accept plain text"
                HTTPProxyCredentials,
                HTTPBlabEmail, "may be empty"
                requestString, "extra user request. Authorization"
                self userAgentString, String crlf,
                'Content-type: application/x-www-form-urlencoded', String crlf,
                'Content-length: ', argString size printString, String crlf,
                'Host: ', httpUrl authority, String crlf.  "blank line automatically added"

        "umur - IE sends argString without a $? and swiki expects so"
        s sendCommand: argString.

        "get the header of the reply"
        list := s getResponseUpTo: String crlf, String crlf ignoring: String
cr. "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: [
                        "umur 6/25/2003 12:58 - If newUrl is relative then we need to make
it absolute."
                        newUrl := (httpUrl newFromRelativeText: newUrl) asString.
                        self flag: #refactor. "get, post, postmultipart are almost doing
the same stuff"
                        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


On Mon, Feb 21, 2011 at 7:54 PM, Paul DeBruicker <[hidden email]> wrote:

> On 02/21/2011 12:33 PM, Otto Behrens wrote:
>>
>> Thanks, Paul.
>>
>> This did not make a difference though. Can you do me a huge favour by
>> trying the example in your environment? If it works there, I know that
>> it has something to do with my environment.
>>
>> Cheers
>> Otto
>
> Hi Otto,
>
> The example works as you describe in your email.
>
> In Pharo, it works fine.
>
> In Gemstone it takes much longer and doesn't work.
>
> If you look at what is being sent to recaptcha.net in the POST using
> Wireshark you can see the error.
>
>
> in pharo the POST data being sent is:
>
> [truncated]
> response=whatever&remoteip=&challenge=03AHJ_Vus2ELMgMly7AajxZfgnKUkdOTJWHT2E-OA6pKs16V82abS8oXANUxYn8ziFLs7gRg_Iy3HMVhH8gMzeI8aO3Y7IZJuQDYh7zV8EeIgIjvIclPUCAOtIVblw1mKnf5Mp93tQSABUdeyzwUpgAtgURAiFpFZOAA&privatekey=6LdUKcESAAAAA
>
>
> in gemstone the POST data being sent is:
>
> challenge=anArray&privatekey=anArray&remoteip=anArray&response=anArray
>
>
> The delay comes from their side.  They wait 240 seconds before deciding to
> let you know that they don't know what to do with the payload.
>
> Hope this helps.
>
> Paul
>
Reply | Threaded
Open this post in threaded view
|

Re: HTTPSocket issue

EstebanLM
Hi,
There is a problem with #httpPostDocument: in gemstone. (Yes, I made the port, and I made a mistake with the API, I'm sorry for that).

In pharo, this api:

TTPSocket
        httpPostDocument: 'http://api-verify.recaptcha.net/verify'
        args: (Dictionary new
                at: 'privatekey' put: #('6LdUKcESAAAAAO8hlc8PyQCRgkS5gXLXTSK4B7b2');
                at: 'remoteip' put: #('');
                at: 'response' put: #('whatever');
                ...

works completely fine. But in Gemstone, the arguments dictionary receives a plain String, not an Array. So,

TTPSocket
        httpPostDocument: 'http://api-verify.recaptcha.net/verify'
        args: (Dictionary new
                at: 'privatekey' put: '6LdUKcESAAAAAO8hlc8PyQCRgkS5gXLXTSK4B7b2';
                at: 'remoteip' put: '';
                at: 'response' put: 'whatever';
                ...

should work better :P
of course, the real fix is to change the Gemstone implementation to match Pharo one, but I didn't find the time yet to do it :(

Cheers,
Esteban

El 21/02/2011, a las 3:46p.m., Otto Behrens escribió:

> Thanks! Just got it going. Saw that the argString: method on GS was
> inlined and behaved differently. Decided to take the Pharo code and
> get that to work in GS and with little modification, it did.
>
> Here's the code (on HTTPSocket):
>
> initHTTPSocket: httpUrl timeoutSecs: timeout ifError: aBlock
> ^self initHTTPSocket: httpUrl wait: timeout ifError: aBlock
>
> 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 type newUrl httpUrl argString |
> httpUrl := Url absoluteFromText: url.
> page := httpUrl fullPath.
> "add arguments"
> argString := argsDict
> ifNotNil: [
> argString := self argString: argsDict.
> argString first = $? ifTrue: [ argString := argString copyFrom: 2
> to: argString size]]
> ifNil: [''].
>
> s := HTTPSocket new.
> s := self initHTTPSocket: httpUrl timeoutSecs: 30 ifError:
> [:errorString | ^errorString].
> s sendCommand: 'POST ', page, ' HTTP/1.0', String crlf,
> (mimeType ifNotNil: ['ACCEPT: ', mimeType, String crlf] ifNil: ['']),
> 'ACCEPT: text/html', String crlf, "Always accept plain text"
> HTTPProxyCredentials,
> HTTPBlabEmail, "may be empty"
> requestString, "extra user request. Authorization"
> self userAgentString, String crlf,
> 'Content-type: application/x-www-form-urlencoded', String crlf,
> 'Content-length: ', argString size printString, String crlf,
> 'Host: ', httpUrl authority, String crlf.  "blank line automatically added"
>
> "umur - IE sends argString without a $? and swiki expects so"
> s sendCommand: argString.
>
> "get the header of the reply"
> list := s getResponseUpTo: String crlf, String crlf ignoring: String
> cr. "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: [
> "umur 6/25/2003 12:58 - If newUrl is relative then we need to make
> it absolute."
> newUrl := (httpUrl newFromRelativeText: newUrl) asString.
> self flag: #refactor. "get, post, postmultipart are almost doing
> the same stuff"
> 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
>
>
> On Mon, Feb 21, 2011 at 7:54 PM, Paul DeBruicker <[hidden email]> wrote:
>> On 02/21/2011 12:33 PM, Otto Behrens wrote:
>>>
>>> Thanks, Paul.
>>>
>>> This did not make a difference though. Can you do me a huge favour by
>>> trying the example in your environment? If it works there, I know that
>>> it has something to do with my environment.
>>>
>>> Cheers
>>> Otto
>>
>> Hi Otto,
>>
>> The example works as you describe in your email.
>>
>> In Pharo, it works fine.
>>
>> In Gemstone it takes much longer and doesn't work.
>>
>> If you look at what is being sent to recaptcha.net in the POST using
>> Wireshark you can see the error.
>>
>>
>> in pharo the POST data being sent is:
>>
>> [truncated]
>> response=whatever&remoteip=&challenge=03AHJ_Vus2ELMgMly7AajxZfgnKUkdOTJWHT2E-OA6pKs16V82abS8oXANUxYn8ziFLs7gRg_Iy3HMVhH8gMzeI8aO3Y7IZJuQDYh7zV8EeIgIjvIclPUCAOtIVblw1mKnf5Mp93tQSABUdeyzwUpgAtgURAiFpFZOAA&privatekey=6LdUKcESAAAAA
>>
>>
>> in gemstone the POST data being sent is:
>>
>> challenge=anArray&privatekey=anArray&remoteip=anArray&response=anArray
>>
>>
>> The delay comes from their side.  They wait 240 seconds before deciding to
>> let you know that they don't know what to do with the payload.
>>
>> Hope this helps.
>>
>> Paul
>>