Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / socket / AppEngineDatagramSocketImpl.java
blobfb1f76d9ad4c947b6671a60343e316b07314d22b
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;
38 /**
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;
49 String descriptor;
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) {
70 return bytes;
73 if (bytes.length == IPV4_LEN) {
74 return new byte[] {
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);
81 void setOption(
82 AppEngineSocketOptions.Option option, Object value) throws SocketException {
83 checkNotClosed();
84 option.validateAndApply(this, value);
87 @Override
88 public void setTimeout(int timeout) {
89 this.timeout = timeout;
92 @Override
93 public void setSocketOptionAsBytes(Option option, byte[] value) throws SocketException {
94 SocketOptionName name = option.getOptName();
95 if (name == null) {
96 return;
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);
107 @Override
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);
116 @Override
117 public Object getOption(int optID) throws SocketException {
118 if (SocketOptions.SO_TIMEOUT == optID) {
119 checkNotClosed();
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)
141 @Override
142 public byte[] getSocketOptionAsBytes(Option option) throws SocketException {
143 checkNotClosed();
144 switch (option) {
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();
152 default: {
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");
172 @Override
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();
188 @Override
189 protected void bind(int lport, InetAddress laddr) throws SocketException {
190 checkNotClosed();
191 BindRequest request = new BindRequest().setSocketDescriptor(descriptor);
192 request.setProxyExternalIp(toAddressPort(laddr, lport));
193 getSocketApiHelper().makeSyncCall("Bind", request, new BindReply(), null);
196 @Override
197 protected void send(DatagramPacket p) throws IOException {
198 checkNotClosed();
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();
208 if (addr != null) {
209 request.setSendTo(toAddressPort(addr, p.getPort()));
211 if (timeout != -1) {
212 request.setTimeoutSeconds(timeout * 0.001);
214 getSocketApiHelper().makeSyncCall("Send", request, new SendReply(), null);
217 @Override
218 protected int peek(InetAddress i) throws IOException {
219 throw new SocketException("#peek(InetAddress) is not implemented.");
222 @Override
223 protected int peekData(DatagramPacket p) throws IOException {
224 checkNotClosed();
225 receiveOrPeek(p, true);
226 return p.getPort();
229 @Override
230 protected void receive(DatagramPacket p) throws IOException {
231 checkNotClosed();
232 receiveOrPeek(p, false);
235 private void receiveOrPeek(DatagramPacket p, boolean isPeek) throws IOException {
236 checkNotClosed();
238 ReceiveReply response = new ReceiveReply();
239 ReceiveRequest request = new ReceiveRequest().setSocketDescriptor(descriptor)
240 .setDataSize(p.getLength());
241 if (timeout != -1) {
242 request.setTimeoutSeconds(timeout * 0.001);
245 if (isPeek) {
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()));
263 @Deprecated
264 @Override
265 protected void setTTL(byte ttl) throws IOException {
266 setTimeToLive(ttl);
269 @Deprecated
270 @Override
271 protected byte getTTL() throws IOException {
272 return (byte) getTimeToLive();
275 @Override
276 protected void setTimeToLive(int ttl) throws IOException {
277 throw new SecurityException("App Engine does not allow multicast setTimeToLive");
280 @Override
281 protected int getTimeToLive() throws IOException {
282 throw new SecurityException("App Engine does not allow multicast getTimeToLive");
285 @Override
286 protected void join(InetAddress inetaddr) throws IOException {
287 throw new SecurityException("App Engine does not allow multicast join");
290 @Override
291 protected void leave(InetAddress inetaddr) throws IOException {
292 throw new SecurityException("App Engine does not allow multicast leave");
295 @Override
296 protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {
297 throw new SecurityException("App Engine does not allow multicast joinGroup");
300 @Override
301 protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {
302 throw new SecurityException("App Engine does not allow multicast leaveGroup");
305 @Override
306 protected void close() {
307 if (descriptor != null) {
308 try {
309 CloseRequest request = new CloseRequest().setSocketDescriptor(descriptor);
310 getSocketApiHelper().makeSyncCall("Close", request, new CloseReply(), null);
311 } catch (SocketException e) {
312 } finally {
313 descriptor = null;