private static FreenetURI insertData(File dataFile) throws IOException { long startInsertTime = System.currentTimeMillis(); InetAddress localhost = InetAddress.getByName("127.0.0.1"); Socket sock = new Socket(localhost, 9481); OutputStream sockOS = sock.getOutputStream(); InputStream sockIS = sock.getInputStream(); System.out.println("Connected to node."); LineReadingInputStream lis = new LineReadingInputStream(sockIS); OutputStreamWriter osw = new OutputStreamWriter(sockOS, "UTF-8"); osw.write( "ClientHello\nExpectedVersion=0.7\nName=BootstrapPullTest-" + System.currentTimeMillis() + "\nEnd\n"); osw.flush(); String name = lis.readLine(65536, 128, true); SimpleFieldSet fs = new SimpleFieldSet(lis, 65536, 128, true, true, false, true); if (!name.equals("NodeHello")) { System.err.println("No NodeHello from insertor node!"); System.exit(EXIT_INSERTER_PROBLEM); } System.out.println("Connected to " + sock); osw.write( "ClientPut\nIdentifier=test-insert\nURI=CHK@\nVerbosity=1023\nUploadFrom=direct\nMaxRetries=-1\nDataLength=" + TEST_SIZE + "\nData\n"); osw.flush(); InputStream is = new FileInputStream(dataFile); FileUtil.copy(is, sockOS, TEST_SIZE); System.out.println("Sent data"); while (true) { name = lis.readLine(65536, 128, true); fs = new SimpleFieldSet(lis, 65536, 128, true, true, false, true); System.out.println("Got FCP message: \n" + name); System.out.print(fs.toOrderedString()); if (name.equals("ProtocolError")) { System.err.println("Protocol error when inserting data."); System.exit(EXIT_INSERTER_PROBLEM); } if (name.equals("PutFailed")) { System.err.println("Insert failed"); System.exit(EXIT_INSERT_FAILED); } if (name.equals("PutSuccessful")) { long endInsertTime = System.currentTimeMillis(); FreenetURI uri = new FreenetURI(fs.get("URI")); System.out.println( "RESULT: Insert took " + (endInsertTime - startInsertTime) + "ms (" + TimeUtil.formatTime(endInsertTime - startInsertTime) + ") to " + uri + " ."); sockOS.close(); sockIS.close(); sock.close(); return uri; } } }
/** Handle an incoming connection. Blocking, obviously. */ public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) { try { InputStream is = new BufferedInputStream(sock.getInputStream(), 4096); LineReadingInputStream lis = new LineReadingInputStream(is); while (true) { String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8 if (firstLine == null) { sock.close(); return; } else if (firstLine.equals("")) { continue; } boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class); if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine); String[] split = firstLine.split(" "); if (split.length != 3) throw new ParseException( "Could not parse request line (split.length=" + split.length + "): " + firstLine); if (!split[2].startsWith("HTTP/1.")) throw new ParseException("Unrecognized protocol " + split[2]); URI uri; try { uri = URIPreEncoder.encodeURI(split[1]).normalize(); if (logMINOR) Logger.minor( ToadletContextImpl.class, "URI: " + uri + " path " + uri.getPath() + " host " + uri.getHost() + " frag " + uri.getFragment() + " port " + uri.getPort() + " query " + uri.getQuery() + " scheme " + uri.getScheme()); } catch (URISyntaxException e) { sendURIParseError(sock.getOutputStream(), true, e); return; } String method = split[0]; MultiValueTable<String, String> headers = new MultiValueTable<String, String>(); while (true) { String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8 if (line == null) { sock.close(); return; } // System.out.println("Length="+line.length()+": "+line); if (line.length() == 0) break; int index = line.indexOf(':'); if (index < 0) { throw new ParseException("Missing ':' in request header field"); } String before = line.substring(0, index).toLowerCase(); String after = line.substring(index + 1); after = after.trim(); headers.put(before, after); } boolean disconnect = shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers) || !container.enablePersistentConnections(); boolean allowPost = container.allowPosts(); BucketFactory bf = container.getBucketFactory(); ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container); ctx.shouldDisconnect = disconnect; /* * copy the data into a bucket now, * before we go into the redirect loop */ Bucket data; boolean methodIsConfigurable = true; String slen = headers.get("content-length"); if (METHODS_MUST_HAVE_DATA.contains(method)) { // <method> must have data methodIsConfigurable = false; if (slen == null) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } } else if (METHODS_CANNOT_HAVE_DATA.contains(method)) { // <method> can not have data methodIsConfigurable = false; if (slen != null) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } } if (slen != null) { long len; try { len = Integer.parseInt(slen); if (len < 0) throw new NumberFormatException("content-length less than 0"); } catch (NumberFormatException e) { ctx.shouldDisconnect = true; ctx.sendReplyHeaders(400, "Bad Request", null, null, -1); return; } if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) { data = bf.makeBucket(len); BucketTools.copyFrom(data, is, len); } else { FileUtil.skipFully(is, len); if (method.equals("POST")) { ctx.sendMethodNotAllowed("POST", true); } else { sendError( sock.getOutputStream(), 403, "Forbidden", "Content not allowed in this configuration", true, null); } ctx.close(); return; } } else { // we're not doing to use it, but we have to keep // the compiler happy data = null; } if (!container.enableExtendedMethodHandling()) { if (!METHODS_RESTRICTED_MODE.contains(method)) { sendError( sock.getOutputStream(), 403, "Forbidden", "Method not allowed in this configuration", true, null); return; } } // Handle it. try { boolean redirect = true; while (redirect) { // don't go around the loop unless set explicitly redirect = false; Toadlet t; try { t = container.findToadlet(uri); } catch (PermanentRedirectException e) { Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString()); break; } if (t == null) { ctx.sendNoToadletError(ctx.shouldDisconnect); break; } // if the Toadlet does not support the method, we don't need to parse the data // also due this pre check a 'NoSuchMethodException' should never appear if (!(t.findSupportedMethods().contains(method))) { ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect); break; } HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method); try { String methodName = "handleMethod" + method; try { Class<? extends Toadlet> c = t.getClass(); Method m = c.getMethod(methodName, HANDLE_PARAMETERS); if (methodIsConfigurable) { AllowData anno = m.getAnnotation(AllowData.class); if (anno == null) { if (data != null) { sendError( sock.getOutputStream(), 400, "Bad Request", "Content not allowed", true, null); ctx.close(); return; } } else if (anno.value()) { if (data == null) { sendError( sock.getOutputStream(), 400, "Bad Request", "Missing Content", true, null); ctx.close(); return; } } } ctx.setActiveToadlet(t); Object arglist[] = new Object[] {uri, req, ctx}; m.invoke(t, arglist); } catch (InvocationTargetException ite) { throw ite.getCause(); } } catch (RedirectException re) { uri = re.newuri; redirect = true; } finally { req.freeParts(); } } if (ctx.shouldDisconnect) { sock.close(); return; } } finally { if (data != null) data.free(); } } } catch (ParseException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("parseErrorWithError", "error", e.getMessage()), true, null); } catch (IOException e1) { // Ignore } } catch (TooLongException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null); } catch (IOException e1) { // Ignore } } catch (IOException e) { // ignore and return } catch (ToadletContextClosedException e) { Logger.error( ToadletContextImpl.class, "ToadletContextClosedException while handling connection!"); } catch (Throwable t) { Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t); try { sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null); } catch (IOException e1) { // ignore and return } } }
/** Handle an incoming connection. Blocking, obviously. */ public static void handle(Socket sock, ToadletContainer container, PageMaker pageMaker) { try { InputStream is = new BufferedInputStream(sock.getInputStream(), 4096); LineReadingInputStream lis = new LineReadingInputStream(is); while (true) { String firstLine = lis.readLine(32768, 128, false); // ISO-8859-1 or US-ASCII, _not_ UTF-8 if (firstLine == null) { sock.close(); return; } else if (firstLine.equals("")) { continue; } boolean logMINOR = Logger.shouldLog(Logger.MINOR, ToadletContextImpl.class); if (logMINOR) Logger.minor(ToadletContextImpl.class, "first line: " + firstLine); String[] split = firstLine.split(" "); if (split.length != 3) throw new ParseException( "Could not parse request line (split.length=" + split.length + "): " + firstLine); if (!split[2].startsWith("HTTP/1.")) throw new ParseException("Unrecognized protocol " + split[2]); URI uri; try { uri = URIPreEncoder.encodeURI(split[1]).normalize(); if (logMINOR) Logger.minor( ToadletContextImpl.class, "URI: " + uri + " path " + uri.getPath() + " host " + uri.getHost() + " frag " + uri.getFragment() + " port " + uri.getPort() + " query " + uri.getQuery() + " scheme " + uri.getScheme()); } catch (URISyntaxException e) { sendURIParseError(sock.getOutputStream(), true, e); return; } String method = split[0]; MultiValueTable<String, String> headers = new MultiValueTable<String, String>(); while (true) { String line = lis.readLine(32768, 128, false); // ISO-8859 or US-ASCII, not UTF-8 if (line == null) { sock.close(); return; } // System.out.println("Length="+line.length()+": "+line); if (line.length() == 0) break; int index = line.indexOf(':'); if (index < 0) { throw new ParseException("Missing ':' in request header field"); } String before = line.substring(0, index).toLowerCase(); String after = line.substring(index + 1); after = after.trim(); headers.put(before, after); } boolean disconnect = shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers) || !container.enablePersistentConnections(); boolean allowPost = container.allowPosts(); BucketFactory bf = container.getBucketFactory(); ToadletContextImpl ctx = new ToadletContextImpl(sock, headers, bf, pageMaker, container); ctx.shouldDisconnect = disconnect; /* * if we're handling a POST, copy the data into a bucket now, * before we go into the redirect loop */ Bucket data; if (method.equals("POST")) { String slen = headers.get("content-length"); if (slen == null) { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("noContentLengthInPOST"), true, null); return; } long len; try { len = Integer.parseInt(slen); if (len < 0) throw new NumberFormatException("content-length less than 0"); } catch (NumberFormatException e) { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("cannotParseContentLengthWithError", "error", e.toString()), true, null); return; } if (allowPost && ((!container.publicGatewayMode()) || ctx.isAllowedFullAccess())) { data = bf.makeBucket(len); BucketTools.copyFrom(data, is, len); } else { FileUtil.skipFully(is, len); ctx.sendMethodNotAllowed("POST", true); ctx.close(); return; } } else { // we're not doing to use it, but we have to keep // the compiler happy data = null; } // Handle it. try { boolean redirect = true; while (redirect) { // don't go around the loop unless set explicitly redirect = false; Toadlet t; try { t = container.findToadlet(uri); } catch (PermanentRedirectException e) { Toadlet.writePermanentRedirect(ctx, "Found elsewhere", e.newuri.toASCIIString()); break; } if (t == null) { ctx.sendNoToadletError(ctx.shouldDisconnect); break; } HTTPRequestImpl req = new HTTPRequestImpl(uri, data, ctx, method); try { if (method.equals("GET")) { ctx.setActiveToadlet(t); t.handleGet(uri, req, ctx); ctx.close(); } else if (method.equals("POST")) { ctx.setActiveToadlet(t); t.handlePost(uri, req, ctx); } else { ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect); ctx.close(); } } catch (RedirectException re) { uri = re.newuri; redirect = true; } finally { req.freeParts(); } } if (ctx.shouldDisconnect) { sock.close(); return; } } finally { if (data != null) data.free(); } } } catch (ParseException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("parseErrorWithError", "error", e.getMessage()), true, null); } catch (IOException e1) { // Ignore } } catch (TooLongException e) { try { sendError( sock.getOutputStream(), 400, "Bad Request", l10n("headersLineTooLong"), true, null); } catch (IOException e1) { // Ignore } } catch (IOException e) { // ignore and return } catch (ToadletContextClosedException e) { Logger.error( ToadletContextImpl.class, "ToadletContextClosedException while handling connection!"); } catch (Throwable t) { Logger.error(ToadletContextImpl.class, "Caught error: " + t + " handling socket", t); try { sendError(sock.getOutputStream(), 500, "Internal Error", t.toString(), true, null); } catch (IOException e1) { // ignore and return } } }