1 // Copyright 2012 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.socket
;
5 import com
.google
.appengine
.api
.socket
.AppEngineSocketOptions
.Option
;
6 import com
.google
.appengine
.api
.socket
.SocketServicePb
.AddressPort
;
7 import com
.google
.appengine
.api
.socket
.SocketServicePb
.BindReply
;
8 import com
.google
.appengine
.api
.socket
.SocketServicePb
.BindRequest
;
9 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CloseReply
;
10 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CloseRequest
;
11 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketReply
;
12 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
;
13 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
.SocketFamily
;
14 import com
.google
.appengine
.api
.socket
.SocketServicePb
.CreateSocketRequest
.SocketProtocol
;
15 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketNameReply
;
16 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketNameRequest
;
17 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketOptionsReply
;
18 import com
.google
.appengine
.api
.socket
.SocketServicePb
.GetSocketOptionsRequest
;
19 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ReceiveReply
;
20 import com
.google
.appengine
.api
.socket
.SocketServicePb
.ReceiveRequest
;
21 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SendReply
;
22 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SendRequest
;
23 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SetSocketOptionsReply
;
24 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SetSocketOptionsRequest
;
25 import com
.google
.appengine
.api
.socket
.SocketServicePb
.SocketOption
.SocketOptionName
;
27 import java
.io
.IOException
;
28 import java
.io
.Serializable
;
29 import java
.net
.DatagramPacket
;
30 import java
.net
.DatagramSocketImpl
;
31 import java
.net
.InetAddress
;
32 import java
.net
.NetworkInterface
;
33 import java
.net
.SocketAddress
;
34 import java
.net
.SocketException
;
35 import java
.net
.SocketOptions
;
36 import java
.util
.Arrays
;
39 * Implements the {@link DatagramSocketImpl} interface for App Engine based datagram sockets.
42 class AppEngineDatagramSocketImpl
43 extends DatagramSocketImpl
implements AppEngineSocketOptionsClient
, Serializable
{
44 private static final int IPV6_LEN
= 16;
45 private static final int IPV4_LEN
= 4;
47 private SocketApiHelper socketHelper
= null;
50 volatile int timeout
= -1;
52 AppEngineDatagramSocketImpl() {
55 AppEngineDatagramSocketImpl(SocketApiHelper socketHelper
) {
56 this.socketHelper
= socketHelper
;
59 SocketApiHelper
getSocketApiHelper() {
60 return socketHelper
== null ? AppEngineSocketImplFactory
.SOCKET_API_HELPER
: socketHelper
;
63 private AddressPort
toAddressPort(InetAddress address
, int port
) {
64 return new AddressPort().setPort(port
).setPackedAddressAsBytes(addrAsIpv6Bytes(address
));
67 private byte[] addrAsIpv6Bytes(InetAddress address
) {
68 byte[] bytes
= address
.getAddress();
69 if (bytes
.length
== IPV6_LEN
) {
73 if (bytes
.length
== IPV4_LEN
) {
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, bytes
[0], bytes
[1], bytes
[2], bytes
[3]};
77 throw new IllegalArgumentException(
78 "address must be IPV4 or IPV6 - unknown size: " + bytes
.length
);
82 AppEngineSocketOptions
.Option option
, Object value
) throws SocketException
{
84 option
.validateAndApply(this, value
);
88 public void setTimeout(int timeout
) {
89 this.timeout
= timeout
;
93 public void setSocketOptionAsBytes(Option option
, byte[] value
) throws SocketException
{
94 SocketOptionName name
= option
.getOptName();
99 SetSocketOptionsRequest request
= new SetSocketOptionsRequest();
100 request
.setSocketDescriptor(descriptor
);
101 request
.addOptions().setLevel(option
.getLevel()).setOption(name
).setValueAsBytes(value
);
103 getSocketApiHelper().makeSyncCall(
104 "SetSocketOptions", request
, new SetSocketOptionsReply(), null);
108 public void setOption(int optID
, Object value
) throws SocketException
{
109 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(optID
);
110 if (option
== null) {
111 throw new SocketException("unrecognized socket option: " + optID
);
113 setOption(option
, value
);
117 public Object
getOption(int optID
) throws SocketException
{
118 if (SocketOptions
.SO_TIMEOUT
== optID
) {
120 return timeout
< 0 ? Integer
.MAX_VALUE
: timeout
;
122 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(optID
);
123 if (option
== null) {
124 throw new SocketException("unrecognized socket option: " + optID
);
126 return getOption(option
);
130 * Returns the value of the given option.
131 * @throws SocketException
133 private Object
getOption(Option option
) throws SocketException
{
134 return option
.getOption(this);
138 * @throws SocketException
139 * @see AppEngineSocketOptionsClient#getSocketOptionAsBytes(Option)
142 public byte[] getSocketOptionAsBytes(Option option
) throws SocketException
{
145 case SO_BINDADDR_OPT
: {
146 GetSocketNameRequest request
= new GetSocketNameRequest();
147 GetSocketNameReply response
= new GetSocketNameReply();
148 request
.setSocketDescriptor(descriptor
);
149 getSocketApiHelper().makeSyncCall("GetSocketName", request
, response
, null);
150 return response
.getProxyExternalIp().getPackedAddressAsBytes();
153 GetSocketOptionsRequest request
= new GetSocketOptionsRequest();
154 GetSocketOptionsReply response
= new GetSocketOptionsReply();
155 request
.setSocketDescriptor(descriptor
);
156 request
.addOptions().setLevel(option
.getLevel()).setOption(option
.getOptName());
157 getSocketApiHelper().makeSyncCall("GetSocketOptions", request
, response
, null);
158 return response
.optionss().get(0).getValueAsBytes();
164 * @throws SocketException if the socket is not open.
166 private void checkNotClosed() throws SocketException
{
167 if (descriptor
== null) {
168 throw new SocketException("socket closed");
173 protected void create() throws SocketException
{
174 if (descriptor
!= null) {
175 throw new SocketException("create may not be called on an already created socket.");
178 CreateSocketRequest request
= new CreateSocketRequest();
179 CreateSocketReply response
= new CreateSocketReply();
181 request
.setFamily(SocketFamily
.IPv6
);
182 request
.setProtocol(SocketProtocol
.UDP
);
184 getSocketApiHelper().makeSyncCall("CreateSocket", request
, response
, null);
185 descriptor
= response
.getSocketDescriptor();
189 protected void bind(int lport
, InetAddress laddr
) throws SocketException
{
191 BindRequest request
= new BindRequest().setSocketDescriptor(descriptor
);
192 request
.setProxyExternalIp(toAddressPort(laddr
, lport
));
193 getSocketApiHelper().makeSyncCall("Bind", request
, new BindReply(), null);
197 protected void send(DatagramPacket p
) throws IOException
{
199 byte[] bytes
= p
.getData();
200 int off
= p
.getOffset();
201 int len
= p
.getLength();
202 if (bytes
.length
!= len
|| off
!= 0) {
203 bytes
= Arrays
.copyOfRange(bytes
, off
, off
+ len
);
205 SendRequest request
= new SendRequest().setSocketDescriptor(descriptor
)
206 .setDataAsBytes(bytes
);
207 InetAddress addr
= p
.getAddress();
209 request
.setSendTo(toAddressPort(addr
, p
.getPort()));
212 request
.setTimeoutSeconds(timeout
* 0.001);
214 getSocketApiHelper().makeSyncCall("Send", request
, new SendReply(), null);
218 protected int peek(InetAddress i
) throws IOException
{
219 throw new SocketException("#peek(InetAddress) is not implemented.");
223 protected int peekData(DatagramPacket p
) throws IOException
{
225 receiveOrPeek(p
, true);
230 protected void receive(DatagramPacket p
) throws IOException
{
232 receiveOrPeek(p
, false);
235 private void receiveOrPeek(DatagramPacket p
, boolean isPeek
) throws IOException
{
238 ReceiveReply response
= new ReceiveReply();
239 ReceiveRequest request
= new ReceiveRequest().setSocketDescriptor(descriptor
)
240 .setDataSize(p
.getLength());
242 request
.setTimeoutSeconds(timeout
* 0.001);
246 request
.setFlags(ReceiveRequest
.Flags
.MSG_PEEK
.getValue());
249 getSocketApiHelper().makeSyncCall("Receive", request
, response
, null);
251 byte readBytes
[] = response
.getDataAsBytes();
253 if (readBytes
.length
> 0) {
254 System
.arraycopy(readBytes
, 0, p
.getData(), p
.getOffset(), readBytes
.length
);
256 p
.setLength(readBytes
.length
);
258 AddressPort addrPort
= response
.getReceivedFrom();
259 p
.setPort(addrPort
.getPort());
260 p
.setAddress(InetAddress
.getByAddress(addrPort
.getPackedAddressAsBytes()));
265 protected void setTTL(byte ttl
) throws IOException
{
271 protected byte getTTL() throws IOException
{
272 return (byte) getTimeToLive();
276 protected void setTimeToLive(int ttl
) throws IOException
{
277 throw new SecurityException("App Engine does not allow multicast setTimeToLive");
281 protected int getTimeToLive() throws IOException
{
282 throw new SecurityException("App Engine does not allow multicast getTimeToLive");
286 protected void join(InetAddress inetaddr
) throws IOException
{
287 throw new SecurityException("App Engine does not allow multicast join");
291 protected void leave(InetAddress inetaddr
) throws IOException
{
292 throw new SecurityException("App Engine does not allow multicast leave");
296 protected void joinGroup(SocketAddress mcastaddr
, NetworkInterface netIf
) throws IOException
{
297 throw new SecurityException("App Engine does not allow multicast joinGroup");
301 protected void leaveGroup(SocketAddress mcastaddr
, NetworkInterface netIf
) throws IOException
{
302 throw new SecurityException("App Engine does not allow multicast leaveGroup");
306 protected void close() {
307 if (descriptor
!= null) {
309 CloseRequest request
= new CloseRequest().setSocketDescriptor(descriptor
);
310 getSocketApiHelper().makeSyncCall("Close", request
, new CloseReply(), null);
311 } catch (SocketException e
) {