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
{
45 private SocketApiHelper socketHelper
= null;
48 volatile int timeout
= -1;
50 AppEngineDatagramSocketImpl() {
53 AppEngineDatagramSocketImpl(SocketApiHelper socketHelper
) {
54 this.socketHelper
= socketHelper
;
57 SocketApiHelper
getSocketApiHelper() {
58 return socketHelper
== null ? AppEngineSocketImplFactory
.SOCKET_API_HELPER
: socketHelper
;
62 AppEngineSocketOptions
.Option option
, Object value
) throws SocketException
{
64 option
.validateAndApply(this, value
);
68 public void setTimeout(int timeout
) {
69 this.timeout
= timeout
;
73 public void setSocketOptionAsBytes(Option option
, byte[] value
) throws SocketException
{
74 SocketOptionName name
= option
.getOptName();
79 SetSocketOptionsRequest request
= new SetSocketOptionsRequest();
80 request
.setSocketDescriptor(descriptor
);
81 request
.addOptions().setLevel(option
.getLevel()).setOption(name
).setValueAsBytes(value
);
83 getSocketApiHelper().makeSyncCall(
84 "SetSocketOptions", request
, new SetSocketOptionsReply(), null);
88 public void setOption(int optID
, Object value
) throws SocketException
{
89 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(optID
);
91 throw new SocketException("unrecognized socket option: " + optID
);
93 setOption(option
, value
);
97 public Object
getOption(int optID
) throws SocketException
{
98 if (SocketOptions
.SO_TIMEOUT
== optID
) {
100 return timeout
< 0 ? Integer
.MAX_VALUE
: timeout
;
102 AppEngineSocketOptions
.Option option
= AppEngineSocketOptions
.getOptionById(optID
);
103 if (option
== null) {
104 throw new SocketException("unrecognized socket option: " + optID
);
106 return getOption(option
);
110 * Returns the value of the given option.
111 * @throws SocketException
113 private Object
getOption(Option option
) throws SocketException
{
114 return option
.getOption(this);
118 * @throws SocketException
119 * @see AppEngineSocketOptionsClient#getSocketOptionAsBytes(Option)
122 public byte[] getSocketOptionAsBytes(Option option
) throws SocketException
{
125 case SO_BINDADDR_OPT
: {
126 GetSocketNameRequest request
= new GetSocketNameRequest();
127 GetSocketNameReply response
= new GetSocketNameReply();
128 request
.setSocketDescriptor(descriptor
);
129 getSocketApiHelper().makeSyncCall("GetSocketName", request
, response
, null);
130 return response
.getProxyExternalIp().getPackedAddressAsBytes();
133 GetSocketOptionsRequest request
= new GetSocketOptionsRequest();
134 GetSocketOptionsReply response
= new GetSocketOptionsReply();
135 request
.setSocketDescriptor(descriptor
);
136 request
.addOptions().setLevel(option
.getLevel()).setOption(option
.getOptName());
137 getSocketApiHelper().makeSyncCall("GetSocketOptions", request
, response
, null);
138 return response
.optionss().get(0).getValueAsBytes();
144 * @throws SocketException if the socket is not open.
146 private void checkNotClosed() throws SocketException
{
147 if (descriptor
== null) {
148 throw new SocketException("socket closed");
153 protected void create() throws SocketException
{
154 if (descriptor
!= null) {
155 throw new SocketException("create may not be called on an already created socket.");
158 CreateSocketRequest request
= new CreateSocketRequest();
159 CreateSocketReply response
= new CreateSocketReply();
161 request
.setFamily(SocketFamily
.IPv6
);
162 request
.setProtocol(SocketProtocol
.UDP
);
164 getSocketApiHelper().makeSyncCall("CreateSocket", request
, response
, null);
165 descriptor
= response
.getSocketDescriptor();
169 protected void bind(int lport
, InetAddress laddr
) throws SocketException
{
171 BindRequest request
= new BindRequest().setSocketDescriptor(descriptor
);
172 request
.setProxyExternalIp(AppEngineSocketUtils
.toAddressPort(laddr
, lport
));
173 getSocketApiHelper().makeSyncCall("Bind", request
, new BindReply(), null);
177 protected void send(DatagramPacket p
) throws IOException
{
179 byte[] bytes
= p
.getData();
180 int off
= p
.getOffset();
181 int len
= p
.getLength();
182 if (bytes
.length
!= len
|| off
!= 0) {
183 bytes
= Arrays
.copyOfRange(bytes
, off
, off
+ len
);
185 SendRequest request
= new SendRequest().setSocketDescriptor(descriptor
)
186 .setDataAsBytes(bytes
);
187 InetAddress addr
= p
.getAddress();
189 request
.setSendTo(AppEngineSocketUtils
.toAddressPort(addr
, p
.getPort()));
192 request
.setTimeoutSeconds(timeout
* 0.001);
194 getSocketApiHelper().makeSyncCall("Send", request
, new SendReply(), null);
198 protected int peek(InetAddress i
) throws IOException
{
199 throw new SocketException("#peek(InetAddress) is not implemented.");
203 protected int peekData(DatagramPacket p
) throws IOException
{
205 receiveOrPeek(p
, true);
210 protected void receive(DatagramPacket p
) throws IOException
{
212 receiveOrPeek(p
, false);
215 private void receiveOrPeek(DatagramPacket p
, boolean isPeek
) throws IOException
{
218 ReceiveReply response
= new ReceiveReply();
219 ReceiveRequest request
= new ReceiveRequest().setSocketDescriptor(descriptor
)
220 .setDataSize(p
.getLength());
222 request
.setTimeoutSeconds(timeout
* 0.001);
226 request
.setFlags(ReceiveRequest
.Flags
.MSG_PEEK
.getValue());
229 getSocketApiHelper().makeSyncCall("Receive", request
, response
, null);
231 byte readBytes
[] = response
.getDataAsBytes();
233 if (readBytes
.length
> 0) {
234 System
.arraycopy(readBytes
, 0, p
.getData(), p
.getOffset(), readBytes
.length
);
236 p
.setLength(readBytes
.length
);
238 AddressPort addrPort
= response
.getReceivedFrom();
239 p
.setPort(addrPort
.getPort());
240 p
.setAddress(InetAddress
.getByAddress(addrPort
.getPackedAddressAsBytes()));
245 protected void setTTL(byte ttl
) throws IOException
{
251 protected byte getTTL() throws IOException
{
252 return (byte) getTimeToLive();
256 protected void setTimeToLive(int ttl
) throws IOException
{
257 throw new SecurityException("App Engine does not allow multicast setTimeToLive");
261 protected int getTimeToLive() throws IOException
{
262 throw new SecurityException("App Engine does not allow multicast getTimeToLive");
266 protected void join(InetAddress inetaddr
) throws IOException
{
267 throw new SecurityException("App Engine does not allow multicast join");
271 protected void leave(InetAddress inetaddr
) throws IOException
{
272 throw new SecurityException("App Engine does not allow multicast leave");
276 protected void joinGroup(SocketAddress mcastaddr
, NetworkInterface netIf
) throws IOException
{
277 throw new SecurityException("App Engine does not allow multicast joinGroup");
281 protected void leaveGroup(SocketAddress mcastaddr
, NetworkInterface netIf
) throws IOException
{
282 throw new SecurityException("App Engine does not allow multicast leaveGroup");
286 protected void close() {
287 if (descriptor
!= null) {
289 CloseRequest request
= new CloseRequest().setSocketDescriptor(descriptor
);
290 getSocketApiHelper().makeSyncCall("Close", request
, new CloseReply(), null);
291 } catch (SocketException e
) {