]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | ||
20 | package org.apache.thrift.transport | |
21 | { | |
22 | ||
23 | import flash.errors.EOFError; | |
24 | import flash.events.Event; | |
25 | import flash.events.IOErrorEvent; | |
26 | import flash.events.ProgressEvent; | |
27 | import flash.events.SecurityErrorEvent; | |
28 | import flash.net.URLLoader; | |
29 | import flash.net.URLLoaderDataFormat; | |
30 | import flash.net.URLRequest; | |
31 | import flash.net.URLRequestMethod; | |
32 | import flash.utils.IDataInput; | |
33 | import flash.utils.IDataOutput; | |
34 | import flash.utils.ByteArray; | |
35 | import flash.net.Socket; | |
36 | import flash.events.EventDispatcher; | |
37 | ||
38 | ||
39 | /** | |
40 | * HTTP implementation of the TTransport interface. Used for working with a | |
41 | * Thrift web services implementation. | |
42 | * Unlike Http Client, it uses a single POST, and chunk-encoding to transfer all messages. | |
43 | */ | |
44 | ||
45 | public class TFullDuplexHttpClient extends TTransport | |
46 | { | |
47 | private var socket:Socket = null; | |
48 | ||
49 | private var host:String; | |
50 | ||
51 | private var port:int; | |
52 | ||
53 | private var resource:String; | |
54 | ||
55 | private var stripped:Boolean = false; | |
56 | ||
57 | private var obuffer:ByteArray = new ByteArray(); | |
58 | ||
59 | private var input:IDataInput; | |
60 | ||
61 | private var output:IDataOutput; | |
62 | ||
63 | private var bytesInChunk:int = 0; | |
64 | ||
65 | private var CRLF:ByteArray = new ByteArray(); | |
66 | ||
67 | private var ioCallback:Function = null; | |
68 | ||
69 | private var eventDispatcher:EventDispatcher = new EventDispatcher(); | |
70 | ||
71 | public function TFullDuplexHttpClient(host:String, port:int, resource:String):void | |
72 | { | |
73 | CRLF.writeByte(13); | |
74 | CRLF.writeByte(10); | |
75 | this.host = host; | |
76 | this.port = port; | |
77 | this.resource = resource; | |
78 | } | |
79 | ||
80 | public override function close():void | |
81 | { | |
82 | this.input = null; | |
83 | this.output = null; | |
84 | this.stripped = false; | |
85 | socket.close() | |
86 | } | |
87 | ||
88 | public override function peek():Boolean | |
89 | { | |
90 | if(socket.connected) | |
91 | { | |
92 | trace("Bytes remained:" + socket.bytesAvailable); | |
93 | return socket.bytesAvailable>0; | |
94 | } | |
95 | return false; | |
96 | } | |
97 | ||
98 | public override function read(buf:ByteArray, off:int, len:int):int | |
99 | { | |
100 | var n1:int = 0, n2:int = 0, n3:int = 0, n4:int = 0, cidx:int = 2; | |
101 | var chunkSize:ByteArray = new ByteArray(); | |
102 | ||
103 | try | |
104 | { | |
105 | while (!stripped) | |
106 | { | |
107 | n1 = n2; | |
108 | n2 = n3; | |
109 | n3 = n4; | |
110 | n4 = input.readByte(); | |
111 | if ((n1 == 13) && (n2 == 10) && (n3 == 13) && (n4 == 10)) | |
112 | { | |
113 | stripped = true; | |
114 | } | |
115 | } | |
116 | ||
117 | // read chunk size | |
118 | if (bytesInChunk == 0) | |
119 | { | |
120 | n1 = input.readByte(); | |
121 | n2 = input.readByte(); | |
122 | ||
123 | chunkSize.writeByte(n1); | |
124 | chunkSize.writeByte(n2); | |
125 | ||
126 | while (!((n1 == 13) && (n2 == 10))) | |
127 | { | |
128 | n1 = n2; | |
129 | n2 = input.readByte(); | |
130 | chunkSize.writeByte(n2); | |
131 | } | |
132 | ||
133 | bytesInChunk = parseInt(chunkSize.toString(), 16); | |
134 | } | |
135 | ||
136 | input.readBytes(buf, off, len); | |
137 | debugBuffer(buf); | |
138 | bytesInChunk -= len; | |
139 | ||
140 | if (bytesInChunk == 0) | |
141 | { | |
142 | // advance the : "\r\n" | |
143 | input.readUTFBytes(2); | |
144 | } | |
145 | return len; | |
146 | } | |
147 | catch (e:EOFError) | |
148 | { | |
149 | trace(e); | |
150 | throw new TTransportError(TTransportError.UNKNOWN, "No more data available."); | |
151 | } | |
152 | catch (e:Error) | |
153 | { | |
154 | trace(e); | |
155 | // WTF?? | |
156 | throw new TTransportError(TTransportError.UNKNOWN, "Bad IO error:" + e); | |
157 | } | |
158 | return 0; | |
159 | } | |
160 | ||
161 | public function debugBuffer(buf:ByteArray):void | |
162 | { | |
163 | var debug:String = "BUFFER >>"; | |
164 | var i:int; | |
165 | for (i = 0; i < buf.length; i++) | |
166 | { | |
167 | debug += buf[i] as int; | |
168 | debug += " "; | |
169 | } | |
170 | ||
171 | trace(debug + "<<"); | |
172 | } | |
173 | ||
174 | public override function write(buf:ByteArray, off:int, len:int):void | |
175 | { | |
176 | obuffer.writeBytes(buf, off, len); | |
177 | } | |
178 | ||
179 | public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void | |
180 | { | |
181 | this.eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); | |
182 | } | |
183 | ||
184 | public override function open():void | |
185 | { | |
186 | this.socket = new Socket(); | |
187 | this.socket.addEventListener(Event.CONNECT, socketConnected); | |
188 | this.socket.addEventListener(IOErrorEvent.IO_ERROR, socketError); | |
189 | this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, socketSecurityError); | |
190 | this.socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler); | |
191 | this.socket.connect(host, port); | |
192 | } | |
193 | ||
194 | public function socketConnected(event:Event):void | |
195 | { | |
196 | this.output = this.socket; | |
197 | this.input = this.socket; | |
198 | this.output.writeUTF("CONNECT " + resource + " HTTP/1.1\n" + "Host: " + host + ":" + port + "\r\n" + "User-Agent: Thrift/AS3\r\n" + "Transfer-Encoding: chunked\r\n" + "content-type: application/x-thrift\r\n" + "Accept: */*\r\n\r\n"); | |
199 | this.eventDispatcher.dispatchEvent(event); | |
200 | } | |
201 | ||
202 | public function socketError(event:IOErrorEvent):void | |
203 | { | |
204 | trace("Error Connecting:" + event); | |
205 | this.close(); | |
206 | if (ioCallback == null) | |
207 | { | |
208 | return; | |
209 | } | |
210 | ioCallback(new TTransportError(TTransportError.UNKNOWN, "IOError: " + event.text)); | |
211 | this.eventDispatcher.dispatchEvent(event); | |
212 | } | |
213 | ||
214 | public function socketSecurityError(event:SecurityErrorEvent):void | |
215 | { | |
216 | trace("Security Error Connecting:" + event); | |
217 | this.close(); | |
218 | this.eventDispatcher.dispatchEvent(event); | |
219 | } | |
220 | ||
221 | public function socketDataHandler(event:ProgressEvent):void | |
222 | { | |
223 | trace("Got Data call:" +ioCallback); | |
224 | if (ioCallback != null) | |
225 | { | |
226 | ioCallback(null); | |
227 | }; | |
228 | this.eventDispatcher.dispatchEvent(event); | |
229 | } | |
230 | ||
231 | public override function flush(callback:Function = null):void | |
232 | { | |
233 | trace("set callback:" + callback); | |
234 | this.ioCallback = callback; | |
235 | this.output.writeUTF(this.obuffer.length.toString(16)); | |
236 | this.output.writeBytes(CRLF); | |
237 | this.output.writeBytes(this.obuffer); | |
238 | this.output.writeBytes(CRLF); | |
239 | this.socket.flush(); | |
240 | // waiting for new Flex sdk 3.5 | |
241 | //this.obuffer.clear(); | |
242 | this.obuffer = new ByteArray(); | |
243 | } | |
244 | ||
245 | public override function isOpen():Boolean | |
246 | { | |
247 | return (this.socket == null ? false : this.socket.connected); | |
248 | } | |
249 | ||
250 | } | |
251 | } |