1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.squareup.okhttp.internal.spdy; 18 19import com.squareup.okhttp.internal.Platform; 20import com.squareup.okhttp.internal.Util; 21import java.io.ByteArrayOutputStream; 22import java.io.Closeable; 23import java.io.DataOutputStream; 24import java.io.IOException; 25import java.io.OutputStream; 26import java.util.List; 27import java.util.zip.Deflater; 28 29/** Write spdy/3 frames. */ 30final class SpdyWriter implements Closeable { 31 final DataOutputStream out; 32 private final ByteArrayOutputStream nameValueBlockBuffer; 33 private final DataOutputStream nameValueBlockOut; 34 35 SpdyWriter(OutputStream out) { 36 this.out = new DataOutputStream(out); 37 38 Deflater deflater = new Deflater(); 39 deflater.setDictionary(SpdyReader.DICTIONARY); 40 nameValueBlockBuffer = new ByteArrayOutputStream(); 41 nameValueBlockOut = new DataOutputStream( 42 Platform.get().newDeflaterOutputStream(nameValueBlockBuffer, deflater, true)); 43 } 44 45 public synchronized void synStream(int flags, int streamId, int associatedStreamId, int priority, 46 int slot, List<String> nameValueBlock) throws IOException { 47 writeNameValueBlockToBuffer(nameValueBlock); 48 int length = 10 + nameValueBlockBuffer.size(); 49 int type = SpdyConnection.TYPE_SYN_STREAM; 50 51 int unused = 0; 52 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 53 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 54 out.writeInt(streamId & 0x7fffffff); 55 out.writeInt(associatedStreamId & 0x7fffffff); 56 out.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff)); 57 nameValueBlockBuffer.writeTo(out); 58 out.flush(); 59 } 60 61 public synchronized void synReply(int flags, int streamId, List<String> nameValueBlock) 62 throws IOException { 63 writeNameValueBlockToBuffer(nameValueBlock); 64 int type = SpdyConnection.TYPE_SYN_REPLY; 65 int length = nameValueBlockBuffer.size() + 4; 66 67 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 68 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 69 out.writeInt(streamId & 0x7fffffff); 70 nameValueBlockBuffer.writeTo(out); 71 out.flush(); 72 } 73 74 public synchronized void headers(int flags, int streamId, List<String> nameValueBlock) 75 throws IOException { 76 writeNameValueBlockToBuffer(nameValueBlock); 77 int type = SpdyConnection.TYPE_HEADERS; 78 int length = nameValueBlockBuffer.size() + 4; 79 80 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 81 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 82 out.writeInt(streamId & 0x7fffffff); 83 nameValueBlockBuffer.writeTo(out); 84 out.flush(); 85 } 86 87 public synchronized void rstStream(int streamId, int statusCode) throws IOException { 88 int flags = 0; 89 int type = SpdyConnection.TYPE_RST_STREAM; 90 int length = 8; 91 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 92 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 93 out.writeInt(streamId & 0x7fffffff); 94 out.writeInt(statusCode); 95 out.flush(); 96 } 97 98 public synchronized void data(int flags, int streamId, byte[] data) throws IOException { 99 int length = data.length; 100 out.writeInt(streamId & 0x7fffffff); 101 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 102 out.write(data); 103 out.flush(); 104 } 105 106 private void writeNameValueBlockToBuffer(List<String> nameValueBlock) throws IOException { 107 nameValueBlockBuffer.reset(); 108 int numberOfPairs = nameValueBlock.size() / 2; 109 nameValueBlockOut.writeInt(numberOfPairs); 110 for (String s : nameValueBlock) { 111 nameValueBlockOut.writeInt(s.length()); 112 nameValueBlockOut.write(s.getBytes("UTF-8")); 113 } 114 nameValueBlockOut.flush(); 115 } 116 117 public synchronized void settings(int flags, Settings settings) throws IOException { 118 int type = SpdyConnection.TYPE_SETTINGS; 119 int size = settings.size(); 120 int length = 4 + size * 8; 121 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 122 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 123 out.writeInt(size); 124 for (int i = 0; i <= Settings.COUNT; i++) { 125 if (!settings.isSet(i)) continue; 126 int settingsFlags = settings.flags(i); 127 out.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff)); 128 out.writeInt(settings.get(i)); 129 } 130 out.flush(); 131 } 132 133 public synchronized void noop() throws IOException { 134 int type = SpdyConnection.TYPE_NOOP; 135 int length = 0; 136 int flags = 0; 137 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 138 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 139 out.flush(); 140 } 141 142 public synchronized void ping(int flags, int id) throws IOException { 143 int type = SpdyConnection.TYPE_PING; 144 int length = 4; 145 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 146 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 147 out.writeInt(id); 148 out.flush(); 149 } 150 151 public synchronized void goAway(int flags, int lastGoodStreamId, int statusCode) 152 throws IOException { 153 int type = SpdyConnection.TYPE_GOAWAY; 154 int length = 8; 155 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 156 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 157 out.writeInt(lastGoodStreamId); 158 out.writeInt(statusCode); 159 out.flush(); 160 } 161 162 public synchronized void windowUpdate(int streamId, int deltaWindowSize) throws IOException { 163 int type = SpdyConnection.TYPE_WINDOW_UPDATE; 164 int flags = 0; 165 int length = 8; 166 out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff); 167 out.writeInt((flags & 0xff) << 24 | length & 0xffffff); 168 out.writeInt(streamId); 169 out.writeInt(deltaWindowSize); 170 out.flush(); 171 } 172 173 @Override public void close() throws IOException { 174 Util.closeAll(out, nameValueBlockOut); 175 } 176} 177