/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okhttp.internal.spdy; import com.squareup.okhttp.internal.Platform; import com.squareup.okhttp.internal.Util; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.zip.Deflater; /** Write spdy/3 frames. */ final class SpdyWriter implements Closeable { final DataOutputStream out; private final ByteArrayOutputStream nameValueBlockBuffer; private final DataOutputStream nameValueBlockOut; SpdyWriter(OutputStream out) { this.out = new DataOutputStream(out); Deflater deflater = new Deflater(); deflater.setDictionary(SpdyReader.DICTIONARY); nameValueBlockBuffer = new ByteArrayOutputStream(); nameValueBlockOut = new DataOutputStream( Platform.get().newDeflaterOutputStream(nameValueBlockBuffer, deflater, true)); } public synchronized void synStream(int flags, int streamId, int associatedStreamId, int priority, int slot, List<String> nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int length = 10 + nameValueBlockBuffer.size(); int type = SpdyConnection.TYPE_SYN_STREAM; int unused = 0; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(streamId & 0x7fffffff); out.writeInt(associatedStreamId & 0x7fffffff); out.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff)); nameValueBlockBuffer.writeTo(out); out.flush(); } public synchronized void synReply(int flags, int streamId, List<String> nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int type = SpdyConnection.TYPE_SYN_REPLY; int length = nameValueBlockBuffer.size() + 4; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(streamId & 0x7fffffff); nameValueBlockBuffer.writeTo(out); out.flush(); } public synchronized void headers(int flags, int streamId, List<String> nameValueBlock) throws IOException { writeNameValueBlockToBuffer(nameValueBlock); int type = SpdyConnection.TYPE_HEADERS; int length = nameValueBlockBuffer.size() + 4; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(streamId & 0x7fffffff); nameValueBlockBuffer.writeTo(out); out.flush(); } public synchronized void rstStream(int streamId, int statusCode) throws IOException { int flags = 0; int type = SpdyConnection.TYPE_RST_STREAM; int length = 8; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(streamId & 0x7fffffff); out.writeInt(statusCode); out.flush(); } public synchronized void data(int flags, int streamId, byte[] data) throws IOException { int length = data.length; out.writeInt(streamId & 0x7fffffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.write(data); out.flush(); } private void writeNameValueBlockToBuffer(List<String> nameValueBlock) throws IOException { nameValueBlockBuffer.reset(); int numberOfPairs = nameValueBlock.size() / 2; nameValueBlockOut.writeInt(numberOfPairs); for (String s : nameValueBlock) { nameValueBlockOut.writeInt(s.length()); nameValueBlockOut.write(s.getBytes("UTF-8")); } nameValueBlockOut.flush(); } public synchronized void settings(int flags, Settings settings) throws IOException { int type = SpdyConnection.TYPE_SETTINGS; int size = settings.size(); int length = 4 + size * 8; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(size); for (int i = 0; i <= Settings.COUNT; i++) { if (!settings.isSet(i)) continue; int settingsFlags = settings.flags(i); out.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff)); out.writeInt(settings.get(i)); } out.flush(); } public synchronized void noop() throws IOException { int type = SpdyConnection.TYPE_NOOP; int length = 0; int flags = 0; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.flush(); } public synchronized void ping(int flags, int id) throws IOException { int type = SpdyConnection.TYPE_PING; int length = 4; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(id); out.flush(); } public synchronized void goAway(int flags, int lastGoodStreamId, int statusCode) throws IOException { int type = SpdyConnection.TYPE_GOAWAY; int length = 8; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(lastGoodStreamId); out.writeInt(statusCode); out.flush(); } public synchronized void windowUpdate(int streamId, int deltaWindowSize) throws IOException { int type = SpdyConnection.TYPE_WINDOW_UPDATE; int flags = 0; int length = 8; out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); out.writeInt((flags & 0xff) << 24 | length & 0xffffff); out.writeInt(streamId); out.writeInt(deltaWindowSize); out.flush(); } @Override public void close() throws IOException { Util.closeAll(out, nameValueBlockOut); } }